From 700e7db689a15314f037f23f5c11f4c8b5ab9671 Mon Sep 17 00:00:00 2001 From: Andrey Vlasovskikh Date: Tue, 16 Apr 2013 16:02:28 +0400 Subject: [PATCH 01/17] Iteration over ancestors via PyClass.getAncestorTypes() Iteration is no longer lazy because the C3 MRO implementation for new-style classes will be eager anyway. --- .../src/com/jetbrains/python/psi/PyClass.java | 11 ++ .../python/psi/impl/PyClassImpl.java | 146 +++++++++++++++++- 2 files changed, 152 insertions(+), 5 deletions(-) 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 62e9f3735bc8..2e07e9ea19e1 100644 --- a/python/psi-api/src/com/jetbrains/python/psi/PyClass.java +++ b/python/psi-api/src/com/jetbrains/python/psi/PyClass.java @@ -9,6 +9,8 @@ import com.intellij.util.ArrayFactory; import com.intellij.util.Processor; import com.jetbrains.python.codeInsight.controlflow.ScopeOwner; import com.jetbrains.python.psi.stubs.PyClassStub; +import com.jetbrains.python.psi.types.PyClassType; +import com.jetbrains.python.psi.types.TypeEvalContext; import org.jetbrains.annotations.NonNls; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; @@ -42,6 +44,9 @@ public interface PyClass extends PsiNameIdentifierOwner, PyStatement, NameDefine @NotNull PsiElement[] getSuperClassElements(); + @NotNull + List getSuperClassTypes(@NotNull TypeEvalContext context); + @NotNull PyClass[] getSuperClasses(); @@ -113,6 +118,12 @@ public interface PyClass extends PsiNameIdentifierOwner, PyStatement, NameDefine Iterable iterateAncestorClasses(); + /** + * Get a list of all ancestor types. + */ + @NotNull + List getAncestorTypes(@NotNull TypeEvalContext context); + /** * Return the method resolution order list for this class. *
diff --git a/python/src/com/jetbrains/python/psi/impl/PyClassImpl.java b/python/src/com/jetbrains/python/psi/impl/PyClassImpl.java index 520bf5d2571f..83116d4a58d5 100644 --- a/python/src/com/jetbrains/python/psi/impl/PyClassImpl.java +++ b/python/src/com/jetbrains/python/psi/impl/PyClassImpl.java @@ -13,7 +13,7 @@ import com.intellij.psi.stubs.StubElement; import com.intellij.psi.tree.TokenSet; import com.intellij.psi.util.*; import com.intellij.util.*; -import com.intellij.util.containers.ContainerUtil; +import com.intellij.util.containers.*; import com.jetbrains.python.*; import com.jetbrains.python.codeInsight.controlflow.ControlFlowCache; import com.jetbrains.python.codeInsight.controlflow.ScopeOwner; @@ -36,6 +36,8 @@ import org.jetbrains.annotations.Nullable; import javax.swing.*; import java.util.*; +import java.util.HashMap; +import java.util.HashSet; /** * @author yole @@ -176,14 +178,29 @@ public class PyClassImpl extends PyPresentableElementImpl implement } public Iterable iterateAncestors() { - // The implementation is manifestly lazy wrt psi scanning and uses stack rather sparingly. - // It must be more efficient on deep and wide hierarchies, but it was more fun than efficiency that produced it. - return new AncestorsIterable(this); + // TODO: Change this method to getAncestorTypes() + // Implementation is no longer lazy, because C3 resolve for new-style classes will not be lazy + final List results = new ArrayList(); + for (PyClassType type : getAncestorTypes(TypeEvalContext.fastStubOnly(null))) { + if (type != null) { + results.add(new PyClassRef(type.getPyClass())); + } + else { + results.add(new PyClassRef(type)); + } + } + return results; } @Override public Iterable iterateAncestorClasses() { - return new AncestorClassesIterable(this); + final List results = new ArrayList(); + for (PyClassType type : getAncestorTypes(TypeEvalContext.fastStubOnly(null))) { + if (type != null) { + results.add(type.getPyClass()); + } + } + return results; } public boolean isSubclass(PyClass parent) { @@ -1111,6 +1128,125 @@ public class PyClassImpl extends PyPresentableElementImpl implement return super.getUseScope(); } + @NotNull + @Override + public List getSuperClassTypes(@NotNull TypeEvalContext context) { + if (PyNames.FAKE_OLD_BASE.equals(getName())) { + return Collections.emptyList(); + } + final PyClassStub stub = getStub(); + final List result = new ArrayList(); + if (stub != null) { + final PsiElement parent = stub.getParentStub().getPsi(); + if (parent instanceof PyFile) { + final PyFile file = (PyFile)parent; + for (PyQualifiedName name : stub.getSuperClasses()) { + result.add(name != null ? classTypeFromQName(name, file, context) : null); + } + } + } + else { + for (PyExpression expression : getSuperClassExpressions()) { + final PyType type = context.getType(expression); + result.add(type instanceof PyClassType ? (PyClassType)type : null); + // TODO: PyUnknownClassType(expression, expression.getName())? + } + } + final PyBuiltinCache builtinCache = PyBuiltinCache.getInstance(this); + if (result.isEmpty() && isValid() && !builtinCache.hasInBuiltins(this)) { + final String implicitSuperName = LanguageLevel.forElement(this).isPy3K() ? PyNames.OBJECT : PyNames.FAKE_OLD_BASE; + final PyClass implicitSuper = builtinCache.getClass(implicitSuperName); + if (implicitSuper != null) { + final PyType type = context.getType(implicitSuper); + if (type instanceof PyClassType) { + result.add((PyClassType)type); + } + } + } + return result; + } + + @NotNull + @Override + public List getAncestorTypes(@NotNull TypeEvalContext context) { + final List results = new ArrayList(); + final List toProcess = new ArrayList(); + final Set seen = new HashSet(); + final Set visited = new HashSet(); + toProcess.add(this); + while (!toProcess.isEmpty()) { + final PyClass cls = toProcess.remove(0); + visited.add(cls); + final List types = cls.getSuperClassTypes(context); + for (PyClassType type : types) { + if (type == null || !seen.contains(type)) { + results.add(type); + seen.add(type); + } + if (type != null) { + // TODO: process PyClassType instead of PyClass + final PyClass superClass = type.getPyClass(); + if (!visited.contains(superClass)) { + toProcess.add(superClass); + } + } + } + } + return results; + } + + @Nullable + private static PsiElement getElementQNamed(@NotNull NameDefiner nameDefiner, @NotNull PyQualifiedName qualifiedName) { + final int componentCount = qualifiedName.getComponentCount(); + final String fullName = qualifiedName.toString(); + if (componentCount == 0) { + return null; + } + else if (componentCount == 1) { + PsiElement element = nameDefiner.getElementNamed(fullName); + if (element == null) { + element = PyBuiltinCache.getInstance(nameDefiner).getByName(fullName); + } + return element; + } + else { + final String name = qualifiedName.getLastComponent(); + final PyQualifiedName containingQName = qualifiedName.removeLastComponent(); + NameDefiner definer = nameDefiner; + for (String component : containingQName.getComponents()) { + PsiElement element = PyUtil.turnDirIntoInit(definer.getElementNamed(component)); + if (element instanceof PyImportElement) { + element = ((PyImportElement)element).resolve(); + } + if (element instanceof NameDefiner) { + definer = (NameDefiner)element; + } + else { + definer = null; + break; + } + } + if (definer != null) { + return definer.getElementNamed(name); + } + return null; + } + } + + @Nullable + private static PyClassType classTypeFromQName(@NotNull PyQualifiedName qualifiedName, @NotNull PyFile containingFile, + @NotNull TypeEvalContext context) { + final PsiElement element = getElementQNamed(containingFile, qualifiedName); + if (element instanceof PyTypedElement) { + final PyType type = context.getType((PyTypedElement)element); + if (type instanceof PyClassType) { + return (PyClassType)type; + } + } + // TODO: PyUnknownClassType(element, qualifiedName)? + return null; + } + private static class AncestorsIterable implements Iterable { private final PyClassImpl myClass; From 0c75d0a255599e7a574d4321b0d24727465fc268 Mon Sep 17 00:00:00 2001 From: Andrey Vlasovskikh Date: Tue, 16 Apr 2013 16:12:18 +0400 Subject: [PATCH 02/17] Removed PyClassRef-based ancestors iterators --- .../python/psi/impl/PyClassImpl.java | 175 ------------------ 1 file changed, 175 deletions(-) diff --git a/python/src/com/jetbrains/python/psi/impl/PyClassImpl.java b/python/src/com/jetbrains/python/psi/impl/PyClassImpl.java index 83116d4a58d5..a709cded9ccd 100644 --- a/python/src/com/jetbrains/python/psi/impl/PyClassImpl.java +++ b/python/src/com/jetbrains/python/psi/impl/PyClassImpl.java @@ -277,44 +277,6 @@ public class PyClassImpl extends PyPresentableElementImpl implement return PyFileImpl.getStringListFromTargetExpression(PyNames.SLOTS, getClassAttributes()); } - protected List getSuperClassesList() { - if (PyNames.FAKE_OLD_BASE.equals(getName())) { - return Collections.emptyList(); - } - - List result = resolveSuperClassesFromStub(); - if (result == null) { - result = new ArrayList(); - final TypeEvalContext context = TypeEvalContext.fastStubOnly(null); - final PyExpression[] superClassExpressions = getSuperClassExpressions(); - for (PyExpression expression : superClassExpressions) { - final PsiElement element = classElementFromExpression(expression); - if (element != null) { - result.add(new PyClassRef(element)); - } - else { - final PyType type = context.getType(expression); - if (type instanceof PyClassType) { - result.add(new PyClassRef((PyClassType)type)); - } - else { - result.add(new PyClassRef((PsiElement)null)); - } - } - } - } - - if (result.size() == 0 && isValid() && !PyBuiltinCache.getInstance(this).hasInBuiltins(this)) { - String implicitSuperclassName = LanguageLevel.forElement(this).isPy3K() ? PyNames.OBJECT : PyNames.FAKE_OLD_BASE; - PyClass implicitSuperclass = PyBuiltinCache.getInstance(this).getClass(implicitSuperclassName); - if (implicitSuperclass != null) { - result.add(new PyClassRef(implicitSuperclass)); - } - } - - return result; - } - @Nullable private List resolveSuperClassesFromStub() { final PyClassStub stub = getStub(); @@ -1246,141 +1208,4 @@ public class PyClassImpl extends PyPresentableElementImpl implement // TODO: PyUnknownClassType(element, qualifiedName)? return null; } - - private static class AncestorsIterable implements Iterable { - private final PyClassImpl myClass; - - public AncestorsIterable(final PyClassImpl pyClass) { - myClass = pyClass; - } - - public Iterator iterator() { - return new AncestorsIterator(myClass); - } - } - - private static class AncestorsIterator implements Iterator { - List pending = new LinkedList(); - private final Set seen; - Iterator percolator; - PyClassRef prefetch = null; - private final PyClassImpl myAClass; - - public AncestorsIterator(PyClassImpl aClass) { - myAClass = aClass; - percolator = myAClass.getSuperClassesList().iterator(); - seen = new HashSet(); - } - - private AncestorsIterator(PyClassImpl AClass, Set seen) { - myAClass = AClass; - this.seen = seen; - percolator = myAClass.getSuperClassesList().iterator(); - } - - public boolean hasNext() { - // due to already-seen filtering, there's no way but to try and see. - if (prefetch != null) return true; - prefetch = getNext(); - return prefetch != null; - } - - public PyClassRef next() { - final PyClassRef nextClass = getNext(); - if (nextClass == null) throw new NoSuchElementException(); - return nextClass; - } - - @Nullable - private PyClassRef getNext() { - iterations: - while (true) { - if (prefetch != null) { - PyClassRef ret = prefetch; - prefetch = null; - return ret; - } - if (percolator.hasNext()) { - PyClassRef it = percolator.next(); - if (seen.contains(it)) { - continue iterations; // loop back is equivalent to return next(); - } - pending.add(it); - seen.add(it); - return it; - } - else { - while (pending.size() > 0) { - PyClassRef it = pending.get(0); - pending.remove(0); - PyClass pyClass = it.getPyClass(); - if (pyClass != null) { - percolator = new AncestorsIterator((PyClassImpl)pyClass, new HashSet(seen)); - continue iterations; - } - } - return null; - } - } - } - - public void remove() { - throw new UnsupportedOperationException(); - } - } - - private static class AncestorClassesIterable implements Iterable { - private final PyClassImpl myClass; - - public AncestorClassesIterable(final PyClassImpl pyClass) { - myClass = pyClass; - } - - public Iterator iterator() { - return new AncestorClassesIterator(new AncestorsIterator(myClass)); - } - } - - private static class AncestorClassesIterator implements Iterator { - private final AncestorsIterator myAncestorsIterator; - private PyClass myNext; - - public AncestorClassesIterator(AncestorsIterator ancestorsIterator) { - myAncestorsIterator = ancestorsIterator; - } - - @Override - public boolean hasNext() { - if (myNext != null) { - return true; - } - while (myAncestorsIterator.hasNext()) { - PyClassRef clsRef = myAncestorsIterator.getNext(); - if (clsRef == null) { - return false; - } - myNext = clsRef.getPyClass(); - if (myNext != null) { - return true; - } - } - return false; - } - - @Nullable - @Override - public PyClass next() { - if (myNext == null) { - if (!hasNext()) return null; - } - PyClass next = myNext; - myNext = null; - return next; - } - - @Override - public void remove() { - throw new UnsupportedOperationException(); - } - } } From 57621dd6067162de64b80539f5478f146f6b4d4e Mon Sep 17 00:00:00 2001 From: Andrey Vlasovskikh Date: Tue, 16 Apr 2013 17:36:40 +0400 Subject: [PATCH 03/17] Refactored exception inheritance inspection tests to highlighting tests --- .../PyExceptionInheritInspection/expected.xml | 8 -------- .../PyExceptionInheritInspection/test.py | 13 +++++++++++++ .../com/jetbrains/python/PythonInspectionsTest.java | 3 +-- 3 files changed, 14 insertions(+), 10 deletions(-) delete mode 100644 python/testData/inspections/PyExceptionInheritInspection/expected.xml create mode 100644 python/testData/inspections/PyExceptionInheritInspection/test.py diff --git a/python/testData/inspections/PyExceptionInheritInspection/expected.xml b/python/testData/inspections/PyExceptionInheritInspection/expected.xml deleted file mode 100644 index 81e336142662..000000000000 --- a/python/testData/inspections/PyExceptionInheritInspection/expected.xml +++ /dev/null @@ -1,8 +0,0 @@ - - - - test.py - 7 - Exception doesn't inherit from base 'Exception' class - - \ No newline at end of file diff --git a/python/testData/inspections/PyExceptionInheritInspection/test.py b/python/testData/inspections/PyExceptionInheritInspection/test.py new file mode 100644 index 000000000000..df9883b912cb --- /dev/null +++ b/python/testData/inspections/PyExceptionInheritInspection/test.py @@ -0,0 +1,13 @@ +def test_basic(): + class A(object): + pass + + class C(A): + pass + + raise C() + + class D(C, Exception): + pass + + raise D() \ No newline at end of file diff --git a/python/testSrc/com/jetbrains/python/PythonInspectionsTest.java b/python/testSrc/com/jetbrains/python/PythonInspectionsTest.java index 73a820b1c342..c6d2f0550c05 100644 --- a/python/testSrc/com/jetbrains/python/PythonInspectionsTest.java +++ b/python/testSrc/com/jetbrains/python/PythonInspectionsTest.java @@ -110,8 +110,7 @@ public class PythonInspectionsTest extends PyTestCase { } public void testPyExceptionInheritInspection() { - LocalInspectionTool inspection = new PyExceptionInheritInspection(); - doTest(getTestName(false), inspection); + doHighlightingTest(PyExceptionInheritInspection.class); } public void testPyDefaultArgumentInspection() { From 4cab00fe4aaa953b3f3fd479ca4241a472481321 Mon Sep 17 00:00:00 2001 From: Andrey Vlasovskikh Date: Tue, 16 Apr 2013 17:54:36 +0400 Subject: [PATCH 04/17] Fixed exception inheritance inspection for unresolved base classes of exceptions (PY-5811) --- .../inspections/PyExceptionInheritInspection.java | 2 +- .../inspections/PyExceptionInheritInspection/test.py | 10 +++++++++- 2 files changed, 10 insertions(+), 2 deletions(-) diff --git a/python/src/com/jetbrains/python/inspections/PyExceptionInheritInspection.java b/python/src/com/jetbrains/python/inspections/PyExceptionInheritInspection.java index bb41d93b8c6c..5019e19f0ff1 100644 --- a/python/src/com/jetbrains/python/inspections/PyExceptionInheritInspection.java +++ b/python/src/com/jetbrains/python/inspections/PyExceptionInheritInspection.java @@ -49,7 +49,7 @@ public class PyExceptionInheritInspection extends PyInspection { PyClass aClass = (PyClass) psiElement; for (PyClassRef pyClass : aClass.iterateAncestors()) { final String name = pyClass.getClassName(); - if ("BaseException".equals(name) || "Exception".equals(name)) { + if (name == null || "BaseException".equals(name) || "Exception".equals(name)) { return; } } diff --git a/python/testData/inspections/PyExceptionInheritInspection/test.py b/python/testData/inspections/PyExceptionInheritInspection/test.py index df9883b912cb..652bb121cdb4 100644 --- a/python/testData/inspections/PyExceptionInheritInspection/test.py +++ b/python/testData/inspections/PyExceptionInheritInspection/test.py @@ -10,4 +10,12 @@ def test_basic(): class D(C, Exception): pass - raise D() \ No newline at end of file + raise D() + + +# PY-5811 +def test_unknown_type_exception(): + Unknown = None + class C(Unknown): pass + + raise C() From 55e1379cb0f90b88f6be43372f30d0cf977b396c Mon Sep 17 00:00:00 2001 From: Andrey Vlasovskikh Date: Wed, 17 Apr 2013 16:51:05 +0400 Subject: [PATCH 05/17] Added common ancestor PyClassLikeType of PyClassType and PyJavaClassType --- .../python/psi/impl/PyJavaClassType.java | 27 ++++++++-- .../src/com/jetbrains/python/psi/PyClass.java | 6 +-- .../python/psi/types/PyClassLikeType.java | 17 ++++++ .../python/psi/types/PyClassType.java | 12 +---- .../python/psi/impl/PyClassImpl.java | 53 +++++++++---------- 5 files changed, 68 insertions(+), 47 deletions(-) create mode 100644 python/psi-api/src/com/jetbrains/python/psi/types/PyClassLikeType.java diff --git a/python/pluginSrc/com/jetbrains/python/psi/impl/PyJavaClassType.java b/python/pluginSrc/com/jetbrains/python/psi/impl/PyJavaClassType.java index c9d212cb6ddd..368f4de8d95e 100644 --- a/python/pluginSrc/com/jetbrains/python/psi/impl/PyJavaClassType.java +++ b/python/pluginSrc/com/jetbrains/python/psi/impl/PyJavaClassType.java @@ -11,9 +11,7 @@ import com.jetbrains.python.psi.PyQualifiedExpression; import com.jetbrains.python.psi.resolve.CompletionVariantsProcessor; import com.jetbrains.python.psi.resolve.PyResolveContext; import com.jetbrains.python.psi.resolve.RatedResolveResult; -import com.jetbrains.python.psi.types.PyCallableType; -import com.jetbrains.python.psi.types.PyType; -import com.jetbrains.python.psi.types.TypeEvalContext; +import com.jetbrains.python.psi.types.*; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; @@ -22,7 +20,7 @@ import java.util.List; /** * @author yole */ -public class PyJavaClassType implements PyCallableType { +public class PyJavaClassType implements PyClassLikeType { private final PsiClass myClass; private final boolean myDefinition; @@ -86,4 +84,25 @@ public class PyJavaClassType implements PyCallableType { } return null; } + + @Override + public boolean isDefinition() { + return myDefinition; + } + + @Override + public PyClassLikeType toInstance() { + return myDefinition ? new PyJavaClassType(myClass, false) : this; + } + + @Nullable + @Override + public String getClassQName() { + return myClass.getQualifiedName(); + } + + @Override + public boolean isValid() { + return myClass.isValid(); + } } 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 2e07e9ea19e1..982252d3e574 100644 --- a/python/psi-api/src/com/jetbrains/python/psi/PyClass.java +++ b/python/psi-api/src/com/jetbrains/python/psi/PyClass.java @@ -9,7 +9,7 @@ import com.intellij.util.ArrayFactory; import com.intellij.util.Processor; import com.jetbrains.python.codeInsight.controlflow.ScopeOwner; import com.jetbrains.python.psi.stubs.PyClassStub; -import com.jetbrains.python.psi.types.PyClassType; +import com.jetbrains.python.psi.types.PyClassLikeType; import com.jetbrains.python.psi.types.TypeEvalContext; import org.jetbrains.annotations.NonNls; import org.jetbrains.annotations.NotNull; @@ -45,7 +45,7 @@ public interface PyClass extends PsiNameIdentifierOwner, PyStatement, NameDefine PsiElement[] getSuperClassElements(); @NotNull - List getSuperClassTypes(@NotNull TypeEvalContext context); + List getSuperClassTypes(@NotNull TypeEvalContext context); @NotNull PyClass[] getSuperClasses(); @@ -122,7 +122,7 @@ public interface PyClass extends PsiNameIdentifierOwner, PyStatement, NameDefine * Get a list of all ancestor types. */ @NotNull - List getAncestorTypes(@NotNull TypeEvalContext context); + List getAncestorTypes(@NotNull TypeEvalContext context); /** * Return the method resolution order list for this class. diff --git a/python/psi-api/src/com/jetbrains/python/psi/types/PyClassLikeType.java b/python/psi-api/src/com/jetbrains/python/psi/types/PyClassLikeType.java new file mode 100644 index 000000000000..02afcc190a37 --- /dev/null +++ b/python/psi-api/src/com/jetbrains/python/psi/types/PyClassLikeType.java @@ -0,0 +1,17 @@ +package com.jetbrains.python.psi.types; + +import org.jetbrains.annotations.Nullable; + +/** + * @author vlan + */ +public interface PyClassLikeType extends PyCallableType { + boolean isDefinition(); + + PyClassLikeType toInstance(); + + @Nullable + String getClassQName(); + + boolean isValid(); +} diff --git a/python/psi-api/src/com/jetbrains/python/psi/types/PyClassType.java b/python/psi-api/src/com/jetbrains/python/psi/types/PyClassType.java index 39a6d40c5ec6..d70ba1b6fb99 100644 --- a/python/psi-api/src/com/jetbrains/python/psi/types/PyClassType.java +++ b/python/psi-api/src/com/jetbrains/python/psi/types/PyClassType.java @@ -3,21 +3,11 @@ package com.jetbrains.python.psi.types; import com.intellij.openapi.util.UserDataHolder; import com.jetbrains.python.psi.PyClass; import org.jetbrains.annotations.NotNull; -import org.jetbrains.annotations.Nullable; /** * @author yole */ -public interface PyClassType extends PyCallableType, UserDataHolder { +public interface PyClassType extends PyClassLikeType, UserDataHolder { @NotNull PyClass getPyClass(); - - boolean isDefinition(); - - PyClassType toInstance(); - - @Nullable - String getClassQName(); - - boolean isValid(); } diff --git a/python/src/com/jetbrains/python/psi/impl/PyClassImpl.java b/python/src/com/jetbrains/python/psi/impl/PyClassImpl.java index a709cded9ccd..40e489083e66 100644 --- a/python/src/com/jetbrains/python/psi/impl/PyClassImpl.java +++ b/python/src/com/jetbrains/python/psi/impl/PyClassImpl.java @@ -26,10 +26,7 @@ import com.jetbrains.python.psi.stubs.PropertyStubStorage; import com.jetbrains.python.psi.stubs.PyClassStub; import com.jetbrains.python.psi.stubs.PyFunctionStub; import com.jetbrains.python.psi.stubs.PyTargetExpressionStub; -import com.jetbrains.python.psi.types.PyClassType; -import com.jetbrains.python.psi.types.PyClassTypeImpl; -import com.jetbrains.python.psi.types.PyType; -import com.jetbrains.python.psi.types.TypeEvalContext; +import com.jetbrains.python.psi.types.*; import com.jetbrains.python.toolbox.Maybe; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; @@ -181,12 +178,12 @@ public class PyClassImpl extends PyPresentableElementImpl implement // TODO: Change this method to getAncestorTypes() // Implementation is no longer lazy, because C3 resolve for new-style classes will not be lazy final List results = new ArrayList(); - for (PyClassType type : getAncestorTypes(TypeEvalContext.fastStubOnly(null))) { - if (type != null) { - results.add(new PyClassRef(type.getPyClass())); + for (PyClassLikeType type : getAncestorTypes(TypeEvalContext.fastStubOnly(null))) { + if (type instanceof PyClassType) { + results.add(new PyClassRef(((PyClassType)type).getPyClass())); } else { - results.add(new PyClassRef(type)); + results.add(new PyClassRef((PyClass)null)); } } return results; @@ -195,9 +192,9 @@ public class PyClassImpl extends PyPresentableElementImpl implement @Override public Iterable iterateAncestorClasses() { final List results = new ArrayList(); - for (PyClassType type : getAncestorTypes(TypeEvalContext.fastStubOnly(null))) { - if (type != null) { - results.add(type.getPyClass()); + for (PyClassLikeType type : getAncestorTypes(TypeEvalContext.fastStubOnly(null))) { + if (type instanceof PyClassType) { + results.add(((PyClassType)type).getPyClass()); } } return results; @@ -1092,12 +1089,12 @@ public class PyClassImpl extends PyPresentableElementImpl implement @NotNull @Override - public List getSuperClassTypes(@NotNull TypeEvalContext context) { + public List getSuperClassTypes(@NotNull TypeEvalContext context) { if (PyNames.FAKE_OLD_BASE.equals(getName())) { return Collections.emptyList(); } final PyClassStub stub = getStub(); - final List result = new ArrayList(); + final List result = new ArrayList(); if (stub != null) { final PsiElement parent = stub.getParentStub().getPsi(); if (parent instanceof PyFile) { @@ -1110,8 +1107,7 @@ public class PyClassImpl extends PyPresentableElementImpl implement else { for (PyExpression expression : getSuperClassExpressions()) { final PyType type = context.getType(expression); - result.add(type instanceof PyClassType ? (PyClassType)type : null); - // TODO: PyUnknownClassType(expression, expression.getName())? + result.add(type instanceof PyClassLikeType ? (PyClassLikeType)type : null); } } final PyBuiltinCache builtinCache = PyBuiltinCache.getInstance(this); @@ -1120,8 +1116,8 @@ public class PyClassImpl extends PyPresentableElementImpl implement final PyClass implicitSuper = builtinCache.getClass(implicitSuperName); if (implicitSuper != null) { final PyType type = context.getType(implicitSuper); - if (type instanceof PyClassType) { - result.add((PyClassType)type); + if (type instanceof PyClassLikeType) { + result.add((PyClassLikeType)type); } } } @@ -1130,24 +1126,24 @@ public class PyClassImpl extends PyPresentableElementImpl implement @NotNull @Override - public List getAncestorTypes(@NotNull TypeEvalContext context) { - final List results = new ArrayList(); + public List getAncestorTypes(@NotNull TypeEvalContext context) { + final List results = new ArrayList(); final List toProcess = new ArrayList(); - final Set seen = new HashSet(); + final Set seen = new HashSet(); final Set visited = new HashSet(); toProcess.add(this); while (!toProcess.isEmpty()) { final PyClass cls = toProcess.remove(0); visited.add(cls); - final List types = cls.getSuperClassTypes(context); - for (PyClassType type : types) { + final List types = cls.getSuperClassTypes(context); + for (PyClassLikeType type : types) { if (type == null || !seen.contains(type)) { results.add(type); seen.add(type); } - if (type != null) { + if (type instanceof PyClassType) { // TODO: process PyClassType instead of PyClass - final PyClass superClass = type.getPyClass(); + final PyClass superClass = ((PyClassType)type).getPyClass(); if (!visited.contains(superClass)) { toProcess.add(superClass); } @@ -1196,16 +1192,15 @@ public class PyClassImpl extends PyPresentableElementImpl implement } @Nullable - private static PyClassType classTypeFromQName(@NotNull PyQualifiedName qualifiedName, @NotNull PyFile containingFile, - @NotNull TypeEvalContext context) { + private static PyClassLikeType classTypeFromQName(@NotNull PyQualifiedName qualifiedName, @NotNull PyFile containingFile, + @NotNull TypeEvalContext context) { final PsiElement element = getElementQNamed(containingFile, qualifiedName); if (element instanceof PyTypedElement) { final PyType type = context.getType((PyTypedElement)element); - if (type instanceof PyClassType) { - return (PyClassType)type; + if (type instanceof PyClassLikeType) { + return (PyClassLikeType)type; } } - // TODO: PyUnknownClassType(element, qualifiedName)? return null; } } From ebab3b5c1ea1a11a78192e66f7a2fe5416d14b20 Mon Sep 17 00:00:00 2001 From: Andrey Vlasovskikh Date: Wed, 17 Apr 2013 17:22:56 +0400 Subject: [PATCH 06/17] Fixed Jython resolve test for Java superclasses --- .../python/psi/impl/PyJavaClassType.java | 11 +++++++ .../python/psi/types/PyClassLikeType.java | 6 ++++ .../python/psi/impl/PyClassImpl.java | 30 +++++++++--------- .../python/psi/types/PyClassTypeImpl.java | 31 +++++++++---------- 4 files changed, 45 insertions(+), 33 deletions(-) diff --git a/python/pluginSrc/com/jetbrains/python/psi/impl/PyJavaClassType.java b/python/pluginSrc/com/jetbrains/python/psi/impl/PyJavaClassType.java index 368f4de8d95e..75fa31e3f3fe 100644 --- a/python/pluginSrc/com/jetbrains/python/psi/impl/PyJavaClassType.java +++ b/python/pluginSrc/com/jetbrains/python/psi/impl/PyJavaClassType.java @@ -15,6 +15,7 @@ import com.jetbrains.python.psi.types.*; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; +import java.util.ArrayList; import java.util.List; /** @@ -101,6 +102,16 @@ public class PyJavaClassType implements PyClassLikeType { return myClass.getQualifiedName(); } + @NotNull + @Override + public List getSuperClassTypes(@NotNull TypeEvalContext context) { + final List result = new ArrayList(); + for (PsiClass cls : myClass.getSupers()) { + result.add(new PyJavaClassType(cls, myDefinition)); + } + return result; + } + @Override public boolean isValid() { return myClass.isValid(); diff --git a/python/psi-api/src/com/jetbrains/python/psi/types/PyClassLikeType.java b/python/psi-api/src/com/jetbrains/python/psi/types/PyClassLikeType.java index 02afcc190a37..0cd121491809 100644 --- a/python/psi-api/src/com/jetbrains/python/psi/types/PyClassLikeType.java +++ b/python/psi-api/src/com/jetbrains/python/psi/types/PyClassLikeType.java @@ -1,7 +1,10 @@ package com.jetbrains.python.psi.types; +import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; +import java.util.List; + /** * @author vlan */ @@ -13,5 +16,8 @@ public interface PyClassLikeType extends PyCallableType { @Nullable String getClassQName(); + @NotNull + List getSuperClassTypes(@NotNull TypeEvalContext context); + boolean isValid(); } diff --git a/python/src/com/jetbrains/python/psi/impl/PyClassImpl.java b/python/src/com/jetbrains/python/psi/impl/PyClassImpl.java index 40e489083e66..1fac9318508b 100644 --- a/python/src/com/jetbrains/python/psi/impl/PyClassImpl.java +++ b/python/src/com/jetbrains/python/psi/impl/PyClassImpl.java @@ -1128,25 +1128,23 @@ public class PyClassImpl extends PyPresentableElementImpl implement @Override public List getAncestorTypes(@NotNull TypeEvalContext context) { final List results = new ArrayList(); - final List toProcess = new ArrayList(); + final List toProcess = new ArrayList(); final Set seen = new HashSet(); - final Set visited = new HashSet(); - toProcess.add(this); + final Set visited = new HashSet(); + final PyType thisType = context.getType(this); + if (thisType instanceof PyClassLikeType) { + toProcess.add((PyClassLikeType)thisType); + } while (!toProcess.isEmpty()) { - final PyClass cls = toProcess.remove(0); - visited.add(cls); - final List types = cls.getSuperClassTypes(context); - for (PyClassLikeType type : types) { - if (type == null || !seen.contains(type)) { - results.add(type); - seen.add(type); + final PyClassLikeType currentType = toProcess.remove(0); + visited.add(currentType); + for (PyClassLikeType superType : currentType.getSuperClassTypes(context)) { + if (superType == null || !seen.contains(superType)) { + results.add(superType); + seen.add(superType); } - if (type instanceof PyClassType) { - // TODO: process PyClassType instead of PyClass - final PyClass superClass = ((PyClassType)type).getPyClass(); - if (!visited.contains(superClass)) { - toProcess.add(superClass); - } + if (superType != null && !visited.contains(superType)) { + toProcess.add(superType); } } } diff --git a/python/src/com/jetbrains/python/psi/types/PyClassTypeImpl.java b/python/src/com/jetbrains/python/psi/types/PyClassTypeImpl.java index a2e66f1bd85a..a93cc4fb3257 100644 --- a/python/src/com/jetbrains/python/psi/types/PyClassTypeImpl.java +++ b/python/src/com/jetbrains/python/psi/types/PyClassTypeImpl.java @@ -20,7 +20,6 @@ import com.jetbrains.python.PyNames; import com.jetbrains.python.codeInsight.PyDynamicMember; import com.jetbrains.python.psi.*; import com.jetbrains.python.psi.impl.PyBuiltinCache; -import com.jetbrains.python.psi.impl.PyTypeProvider; import com.jetbrains.python.psi.impl.ResolveResultList; import com.jetbrains.python.psi.resolve.*; import com.jetbrains.python.psi.stubs.PyClassNameIndex; @@ -93,6 +92,12 @@ public class PyClassTypeImpl extends UserDataHolderBase implements PyClassType { return myClass.getQualifiedName(); } + @NotNull + @Override + public List getSuperClassTypes(@NotNull TypeEvalContext context) { + return myClass.getSuperClassTypes(context); + } + @Nullable public List resolveMember(@NotNull final String name, @Nullable PyExpression location, @@ -165,28 +170,21 @@ public class PyClassTypeImpl extends UserDataHolderBase implements PyClassType { return ResolveResultList.to(classMember); } - for (PyClassRef superClass : myClass.iterateAncestors()) { - final PyClass pyClass = superClass.getPyClass(); - final PsiElement element = superClass.getElement(); - final PyClassType type = superClass.getType(); - if (pyClass != null) { - PsiElement superMember = resolveClassMember(pyClass, myIsDefinition, name, null); + for (PyClassLikeType type : myClass.getAncestorTypes(resolveContext.getTypeEvalContext())) { + if (type instanceof PyClassType) { + PsiElement superMember = resolveClassMember(((PyClassType)type).getPyClass(), myIsDefinition, name, null); if (superMember != null) { return ResolveResultList.to(superMember); } } - else if (element != null) { - for (PyTypeProvider typeProvider : Extensions.getExtensions(PyTypeProvider.EP_NAME)) { - final PyType refType = typeProvider.getReferenceType(element, resolveContext.getTypeEvalContext(), myClass); - if (refType != null) { - return refType.resolveMember(name, location, direction, resolveContext); - } + if (type != null) { + final List results = type.resolveMember(name, location, direction, resolveContext); + if (results != null && !results.isEmpty()) { + return results; } } - else if (type != null) { - return type.resolveMember(name, location, direction, resolveContext); - } } + if (isDefinition() && myClass.isNewStyleClass()) { PyClassType typeType = getMetaclassType(); if (typeType != null) { @@ -214,7 +212,6 @@ public class PyClassTypeImpl extends UserDataHolderBase implements PyClassType { } } - return Collections.emptyList(); } From d7b81f04d4229a295ed6d702c566441f90b9f3d3 Mon Sep 17 00:00:00 2001 From: Andrey Vlasovskikh Date: Wed, 17 Apr 2013 17:23:23 +0400 Subject: [PATCH 07/17] Fixed test data files capitalization --- .../jython/{callableJavaClass.py => CallableJavaClass.py} | 0 .../jython/{callableStaticMethod.py => CallableStaticMethod.py} | 0 2 files changed, 0 insertions(+), 0 deletions(-) rename python/testData/highlighting/jython/{callableJavaClass.py => CallableJavaClass.py} (100%) rename python/testData/highlighting/jython/{callableStaticMethod.py => CallableStaticMethod.py} (100%) diff --git a/python/testData/highlighting/jython/callableJavaClass.py b/python/testData/highlighting/jython/CallableJavaClass.py similarity index 100% rename from python/testData/highlighting/jython/callableJavaClass.py rename to python/testData/highlighting/jython/CallableJavaClass.py diff --git a/python/testData/highlighting/jython/callableStaticMethod.py b/python/testData/highlighting/jython/CallableStaticMethod.py similarity index 100% rename from python/testData/highlighting/jython/callableStaticMethod.py rename to python/testData/highlighting/jython/CallableStaticMethod.py From e6d662584d5dbaf9e0bfdcb74944b2e848562676 Mon Sep 17 00:00:00 2001 From: Andrey Vlasovskikh Date: Wed, 17 Apr 2013 17:52:37 +0400 Subject: [PATCH 08/17] Removed members provider for SQLAlchemy declarative_base It can be reimplemented as a type provider that returns a class type with declarative_base fields. Removed after a discussion with ktisha. --- python/src/META-INF/python-plugin-common.xml | 1 - 1 file changed, 1 deletion(-) diff --git a/python/src/META-INF/python-plugin-common.xml b/python/src/META-INF/python-plugin-common.xml index bd47039d2fce..5b5fbff61667 100644 --- a/python/src/META-INF/python-plugin-common.xml +++ b/python/src/META-INF/python-plugin-common.xml @@ -893,7 +893,6 @@ - From 7845bc91cabaffdac30a1ded76de42d233c59f6c Mon Sep 17 00:00:00 2001 From: Andrey Vlasovskikh Date: Wed, 17 Apr 2013 18:42:30 +0400 Subject: [PATCH 09/17] Removed PyClassRef and PyClass.iterateAncestors() in favor of PyClass.getAncestorTypes() --- .../src/com/jetbrains/python/psi/PyClass.java | 6 - .../com/jetbrains/python/psi/PyClassRef.java | 99 --------------- .../PythonDocumentationProvider.java | 11 +- .../PyExceptionInheritInspection.java | 8 +- .../PyUnresolvedReferencesInspection.java | 3 +- .../src/com/jetbrains/python/psi/PyUtil.java | 10 +- .../python/psi/impl/PyClassImpl.java | 117 +++--------------- .../python/psi/types/PyClassTypeImpl.java | 21 ++-- .../python/testing/PythonUnitTestUtil.java | 33 +++-- .../PythonAtTestConfigurationProducer.java | 7 +- .../python/testing/pytest/PyTestUtil.java | 8 +- .../com/jetbrains/python/PyStubsTest.java | 4 +- 12 files changed, 75 insertions(+), 252 deletions(-) delete mode 100644 python/psi-api/src/com/jetbrains/python/psi/PyClassRef.java 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 982252d3e574..9fc66ae306c3 100644 --- a/python/psi-api/src/com/jetbrains/python/psi/PyClass.java +++ b/python/psi-api/src/com/jetbrains/python/psi/PyClass.java @@ -110,12 +110,6 @@ public interface PyClass extends PsiNameIdentifierOwner, PyStatement, NameDefine */ boolean isNewStyleClass(); - /** - * A lazy way to list ancestor classes width first, *not* in method-resolution order. - * @return an iterable of ancestor classes. - */ - Iterable iterateAncestors(); - Iterable iterateAncestorClasses(); /** diff --git a/python/psi-api/src/com/jetbrains/python/psi/PyClassRef.java b/python/psi-api/src/com/jetbrains/python/psi/PyClassRef.java deleted file mode 100644 index 950972b8f671..000000000000 --- a/python/psi-api/src/com/jetbrains/python/psi/PyClassRef.java +++ /dev/null @@ -1,99 +0,0 @@ -package com.jetbrains.python.psi; - -import com.intellij.psi.PsiElement; -import com.jetbrains.python.psi.impl.PyQualifiedName; -import com.jetbrains.python.psi.types.PyClassType; -import org.jetbrains.annotations.Nullable; - -/** - * @author yole - */ -public class PyClassRef { - @Nullable private final PsiElement myElement; - @Nullable private final String myQName; - @Nullable private final PyClassType myType; - - public PyClassRef(@Nullable PsiElement element) { - myElement = element; - myQName = null; - myType = null; - } - - public PyClassRef(@Nullable String qName) { - myElement = null; - myQName = qName; - myType = null; - } - - public PyClassRef(@Nullable PyClassType type) { - myElement = null; - myQName = null; - myType = type; - } - - @Nullable - public PyClass getPyClass() { - if (myElement instanceof PyClass) { - return (PyClass) myElement; - } - return null; - } - - @Nullable - public PsiElement getElement() { - return myElement; - } - - @Nullable - public PyClassType getType() { - return myType; - } - - @Nullable - public String getClassName() { - if (myElement instanceof PyClass) { - return ((PyClass)myElement).getName(); - } - else if (myQName != null) { - final PyQualifiedName qname = PyQualifiedName.fromDottedString(myQName); - if (qname != null) { - return qname.getLastComponent(); - } - } - else if (myType != null) { - return myType.getName(); - } - return null; - } - - @Nullable - public String getQualifiedName() { - if (myElement instanceof PyClass) { - return ((PyClass)myElement).getQualifiedName(); - } - else if (myQName != null) { - return myQName; - } - else if (myType != null) { - return myType.getName(); - } - return null; - } - - @Override - public boolean equals(Object o) { - if (this == o) return true; - if (o == null || getClass() != o.getClass()) return false; - - PyClassRef that = (PyClassRef)o; - - if (myElement != null ? !myElement.equals(that.myElement) : that.myElement != null) return false; - - return true; - } - - @Override - public int hashCode() { - return myElement != null ? myElement.hashCode() : 0; - } -} diff --git a/python/src/com/jetbrains/python/documentation/PythonDocumentationProvider.java b/python/src/com/jetbrains/python/documentation/PythonDocumentationProvider.java index 2eef98823142..6d27691521bc 100644 --- a/python/src/com/jetbrains/python/documentation/PythonDocumentationProvider.java +++ b/python/src/com/jetbrains/python/documentation/PythonDocumentationProvider.java @@ -27,10 +27,7 @@ import com.jetbrains.python.psi.*; import com.jetbrains.python.psi.impl.PyBuiltinCache; import com.jetbrains.python.psi.impl.PyQualifiedName; import com.jetbrains.python.psi.resolve.QualifiedNameFinder; -import com.jetbrains.python.psi.types.PyClassType; -import com.jetbrains.python.psi.types.PyType; -import com.jetbrains.python.psi.types.PyTypeParser; -import com.jetbrains.python.psi.types.TypeEvalContext; +import com.jetbrains.python.psi.types.*; import com.jetbrains.python.toolbox.ChainIterable; import com.jetbrains.python.toolbox.FP; import org.apache.commons.httpclient.HttpClient; @@ -316,9 +313,9 @@ public class PythonDocumentationProvider extends AbstractDocumentationProvider i PyClass cls = inferContainingClassOf(context); if (cls != null) { String desired_name = link.substring(LINK_TYPE_PARENT.length()); - for (PyClassRef parent : cls.iterateAncestors()) { - final String parent_name = parent.getClassName(); - if (parent_name != null && parent_name.equals(desired_name)) return parent.getPyClass(); + for (PyClass parent : cls.iterateAncestorClasses()) { + final String parent_name = parent.getName(); + if (parent_name != null && parent_name.equals(desired_name)) return parent; } } } diff --git a/python/src/com/jetbrains/python/inspections/PyExceptionInheritInspection.java b/python/src/com/jetbrains/python/inspections/PyExceptionInheritInspection.java index 5019e19f0ff1..e57ac5e0f32e 100644 --- a/python/src/com/jetbrains/python/inspections/PyExceptionInheritInspection.java +++ b/python/src/com/jetbrains/python/inspections/PyExceptionInheritInspection.java @@ -6,6 +6,7 @@ import com.intellij.psi.PsiElement; import com.intellij.psi.PsiElementVisitor; import com.jetbrains.python.PyBundle; import com.jetbrains.python.psi.*; +import com.jetbrains.python.psi.types.PyClassLikeType; import org.jetbrains.annotations.Nls; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; @@ -47,8 +48,11 @@ public class PyExceptionInheritInspection extends PyInspection { PsiElement psiElement = ((PyReferenceExpression)callee).getReference(resolveWithoutImplicits()).resolve(); if (psiElement instanceof PyClass) { PyClass aClass = (PyClass) psiElement; - for (PyClassRef pyClass : aClass.iterateAncestors()) { - final String name = pyClass.getClassName(); + for (PyClassLikeType type : aClass.getAncestorTypes(myTypeEvalContext)) { + if (type == null) { + return; + } + final String name = type.getName(); if (name == null || "BaseException".equals(name) || "Exception".equals(name)) { return; } diff --git a/python/src/com/jetbrains/python/inspections/PyUnresolvedReferencesInspection.java b/python/src/com/jetbrains/python/inspections/PyUnresolvedReferencesInspection.java index e292d30494e2..3d694e7a1066 100644 --- a/python/src/com/jetbrains/python/inspections/PyUnresolvedReferencesInspection.java +++ b/python/src/com/jetbrains/python/inspections/PyUnresolvedReferencesInspection.java @@ -134,8 +134,7 @@ public class PyUnresolvedReferencesInspection extends PyInspection { final List slots = pyClass.getSlots(); final String attrName = node.getReferencedName(); if (slots != null && !slots.contains(attrName) && !slots.contains(PyNames.DICT)) { - for (PyClassRef ref : pyClass.iterateAncestors()) { - final PyClass ancestor = ref.getPyClass(); + for (PyClass ancestor : pyClass.iterateAncestorClasses()) { if (ancestor == null) { return; } diff --git a/python/src/com/jetbrains/python/psi/PyUtil.java b/python/src/com/jetbrains/python/psi/PyUtil.java index 39a18fbdfcd7..528ff6a5151a 100644 --- a/python/src/com/jetbrains/python/psi/PyUtil.java +++ b/python/src/com/jetbrains/python/psi/PyUtil.java @@ -618,8 +618,8 @@ public class PyUtil { } public static boolean hasUnresolvedAncestors(@NotNull PyClass cls) { - for (PyClassRef classRef : cls.iterateAncestors()) { - if (classRef.getPyClass() == null && classRef.getType() == null) { + for (PyClassLikeType type : cls.getAncestorTypes(TypeEvalContext.fastStubOnly(null))) { + if (type == null) { return true; } } @@ -959,8 +959,10 @@ public class PyUtil { if (isBaseException(pyClass.getQualifiedName())) { return true; } - for (PyClassRef superclass : pyClass.iterateAncestors()) { - if (isBaseException(superclass.getQualifiedName())) return true; + for (PyClassLikeType type : pyClass.getAncestorTypes(TypeEvalContext.fastStubOnly(null))) { + if (type != null && isBaseException(type.getClassQName())) { + return true; + } } return false; } diff --git a/python/src/com/jetbrains/python/psi/impl/PyClassImpl.java b/python/src/com/jetbrains/python/psi/impl/PyClassImpl.java index 1fac9318508b..02ccb6b82831 100644 --- a/python/src/com/jetbrains/python/psi/impl/PyClassImpl.java +++ b/python/src/com/jetbrains/python/psi/impl/PyClassImpl.java @@ -174,21 +174,6 @@ public class PyClassImpl extends PyPresentableElementImpl implement return expression; } - public Iterable iterateAncestors() { - // TODO: Change this method to getAncestorTypes() - // Implementation is no longer lazy, because C3 resolve for new-style classes will not be lazy - final List results = new ArrayList(); - for (PyClassLikeType type : getAncestorTypes(TypeEvalContext.fastStubOnly(null))) { - if (type instanceof PyClassType) { - results.add(new PyClassRef(((PyClassType)type).getPyClass())); - } - else { - results.add(new PyClassRef((PyClass)null)); - } - } - return results; - } - @Override public Iterable iterateAncestorClasses() { final List results = new ArrayList(); @@ -215,8 +200,10 @@ public class PyClassImpl extends PyPresentableElementImpl implement if (superClassQName.equals(getQualifiedName())) { return true; } - for (PyClassRef superclass : iterateAncestors()) { - if (superClassQName.equals(superclass.getQualifiedName())) return true; + for (PyClassLikeType type : getAncestorTypes(TypeEvalContext.fastStubOnly(null))) { + if (type != null && superClassQName.equals(type.getClassQName())) { + return true; + } } return false; } @@ -274,85 +261,19 @@ public class PyClassImpl extends PyPresentableElementImpl implement return PyFileImpl.getStringListFromTargetExpression(PyNames.SLOTS, getClassAttributes()); } - @Nullable - private List resolveSuperClassesFromStub() { - final PyClassStub stub = getStub(); - if (stub == null) { - return null; - } - // stub-based resolve currently works correctly only with classes in file level - final PsiElement parent = stub.getParentStub().getPsi(); - if (!(parent instanceof PyFile)) { - // TODO[yole] handle this case - return null; - } - - List result = new ArrayList(); - for (PyQualifiedName qualifiedName : stub.getSuperClasses()) { - result.add(classRefFromQName((NameDefiner)parent, qualifiedName)); - } - return result; - } - - private static PyClassRef classRefFromQName(NameDefiner parent, PyQualifiedName qualifiedName) { - if (qualifiedName == null) { - return new PyClassRef((String)null); - } - NameDefiner currentParent = parent; - for (String component : qualifiedName.getComponents()) { - PsiElement element = currentParent.getElementNamed(component); - element = PyUtil.turnDirIntoInit(element); - if (element instanceof PyImportElement) { - element = ((PyImportElement)element).resolve(); - } - if (!(element instanceof NameDefiner)) { - currentParent = null; - break; - } - currentParent = (NameDefiner)element; - } - - if (currentParent != null) { - return new PyClassRef(currentParent); - } - if (qualifiedName.getComponentCount() == 1) { - final PyClass builtinClass = PyBuiltinCache.getInstance(parent).getClass(qualifiedName.getComponents().get(0)); - if (builtinClass != null) { - return new PyClassRef(builtinClass); - } - } - return new PyClassRef(qualifiedName.toString()); - } - @NotNull public PyClass[] getSuperClasses() { - final PyClassStub stub = getStub(); - if (stub != null) { - final List pyClasses = resolveSuperClassesFromStub(); - if (pyClasses == null) { - return EMPTY_ARRAY; - } - List result = new ArrayList(); - for (PyClassRef clsRef : pyClasses) { - PyClass pyClass = clsRef.getPyClass(); - if (pyClass != null) { - result.add(pyClass); - } - } - return result.toArray(new PyClass[result.size()]); + final List superTypes = getSuperClassTypes(TypeEvalContext.fastStubOnly(null)); + if (superTypes.isEmpty()) { + return EMPTY_ARRAY; } - - PsiElement[] superClassElements = getSuperClassElements(); - if (superClassElements.length > 0) { - List result = new ArrayList(); - for (PsiElement element : superClassElements) { - if (element instanceof PyClass) { - result.add((PyClass)element); - } + final List result = new ArrayList(); + for (PyClassLikeType type : superTypes) { + if (type instanceof PyClassType) { + result.add(((PyClassType)type).getPyClass()); } - return result.toArray(new PyClass[result.size()]); } - return EMPTY_ARRAY; + return result.toArray(new PyClass[result.size()]); } @@ -963,15 +884,17 @@ 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 (PyClassRef ancestor : iterateAncestors()) { - final PyClass pyClass = ancestor.getPyClass(); - if (pyClass == null) { + for (PyClassLikeType type : getAncestorTypes(TypeEvalContext.fastStubOnly(null))) { + if (type == null) { // unknown, assume new-style class return true; } - if (pyClass == objClass) return true; - if (hasNewStyleMetaClass(pyClass)) { - return true; + if (type instanceof PyClassType) { + final PyClass pyClass = ((PyClassType)type).getPyClass(); + if (pyClass == objClass) return true; + if (hasNewStyleMetaClass(pyClass)) { + return true; + } } } return false; diff --git a/python/src/com/jetbrains/python/psi/types/PyClassTypeImpl.java b/python/src/com/jetbrains/python/psi/types/PyClassTypeImpl.java index a93cc4fb3257..1974fe5e4ad7 100644 --- a/python/src/com/jetbrains/python/psi/types/PyClassTypeImpl.java +++ b/python/src/com/jetbrains/python/psi/types/PyClassTypeImpl.java @@ -122,6 +122,7 @@ public class PyClassTypeImpl extends UserDataHolderBase implements PyClassType { @Nullable PyExpression location, @NotNull AccessDirection direction, @NotNull PyResolveContext resolveContext) { + final TypeEvalContext context = resolveContext.getTypeEvalContext(); PsiElement classMember = resolveByOverridingMembersProviders(this, name); //overriding members provers have priority to normal resolve if (classMember != null) { return ResolveResultList.to(classMember); @@ -147,11 +148,11 @@ public class PyClassTypeImpl extends UserDataHolderBase implements PyClassType { } } - if ("super".equals(getClassQName()) && isBuiltin(resolveContext.getTypeEvalContext()) && location instanceof PyCallExpression) { + if ("super".equals(getClassQName()) && isBuiltin(context) && location instanceof PyCallExpression) { // methods of super() call are not of class super! PyExpression first_arg = ((PyCallExpression)location).getArgument(0, PyExpression.class); if (first_arg != null) { // the usual case: first arg is the derived class that super() is proxying for - PyType first_arg_type = resolveContext.getTypeEvalContext().getType(first_arg); + PyType first_arg_type = context.getType(first_arg); if (first_arg_type instanceof PyClassType) { PyClass derived_class = ((PyClassType)first_arg_type).getPyClass(); final Iterator base_it = derived_class.iterateAncestorClasses().iterator(); @@ -170,7 +171,7 @@ public class PyClassTypeImpl extends UserDataHolderBase implements PyClassType { return ResolveResultList.to(classMember); } - for (PyClassLikeType type : myClass.getAncestorTypes(resolveContext.getTypeEvalContext())) { + for (PyClassLikeType type : myClass.getAncestorTypes(context)) { if (type instanceof PyClassType) { PsiElement superMember = resolveClassMember(((PyClassType)type).getPyClass(), myIsDefinition, name, null); if (superMember != null) { @@ -201,13 +202,15 @@ public class PyClassTypeImpl extends UserDataHolderBase implements PyClassType { return ResolveResultList.to(classMember); } - for (PyClassRef superClass : myClass.iterateAncestors()) { - final PyClass pyClass = superClass.getPyClass(); - if (pyClass != null) { - PsiElement superMember = resolveByMembersProviders(new PyClassTypeImpl(pyClass, isDefinition()), name); + for (PyClassLikeType type : myClass.getAncestorTypes(context)) { + if (type instanceof PyClassType) { + final PyClass pyClass = ((PyClassType)type).getPyClass(); + if (pyClass != null) { + PsiElement superMember = resolveByMembersProviders(new PyClassTypeImpl(pyClass, isDefinition()), name); - if (superMember != null) { - return ResolveResultList.to(superMember); + if (superMember != null) { + return ResolveResultList.to(superMember); + } } } } diff --git a/python/src/com/jetbrains/python/testing/PythonUnitTestUtil.java b/python/src/com/jetbrains/python/testing/PythonUnitTestUtil.java index 5cdeafb82ffb..9e7d1da87091 100644 --- a/python/src/com/jetbrains/python/testing/PythonUnitTestUtil.java +++ b/python/src/com/jetbrains/python/testing/PythonUnitTestUtil.java @@ -6,6 +6,8 @@ import com.intellij.psi.PsiElement; import com.intellij.psi.PsiFile; import com.intellij.util.containers.Stack; import com.jetbrains.python.psi.*; +import com.jetbrains.python.psi.types.PyClassLikeType; +import com.jetbrains.python.psi.types.TypeEvalContext; import org.jetbrains.annotations.NotNull; import java.util.*; @@ -48,11 +50,8 @@ public class PythonUnitTestUtil { } private static boolean isUnitTestCaseClass(PyClass cls, HashSet testQualifiedNames) { - for (PyClassRef ancestor : cls.iterateAncestors()) { - if (ancestor == null) continue; - - String qName = ancestor.getQualifiedName(); - if (testQualifiedNames.contains(qName)) { + for (PyClassLikeType type : cls.getAncestorTypes(TypeEvalContext.fastStubOnly(null))) { + if (type != null && testQualifiedNames.contains(type.getClassQName())) { return true; } } @@ -122,21 +121,19 @@ public class PythonUnitTestUtil { } public static boolean isTestCaseClass(@NotNull PyClass cls, Set testQualifiedNames) { - for (PyClassRef ancestor : cls.iterateAncestors()) { - String qName = ancestor.getQualifiedName(); - if (qName == null) continue; - if (testQualifiedNames.contains(qName)) { - return true; - } - String clsName = cls.getQualifiedName(); - String[] names = clsName.split("\\."); - clsName = names[names.length - 1]; - - if (TEST_MATCH_PATTERN.matcher(clsName).find()) { - return true; + for (PyClassLikeType type : cls.getAncestorTypes(TypeEvalContext.fastStubOnly(null))) { + if (type != null) { + if (testQualifiedNames.contains(type.getClassQName())) { + return true; + } + String clsName = cls.getQualifiedName(); + String[] names = clsName.split("\\."); + clsName = names[names.length - 1]; + if (TEST_MATCH_PATTERN.matcher(clsName).find()) { + return true; + } } } - return false; } } diff --git a/python/src/com/jetbrains/python/testing/attest/PythonAtTestConfigurationProducer.java b/python/src/com/jetbrains/python/testing/attest/PythonAtTestConfigurationProducer.java index 540e0a2fcd49..c9c49e055f98 100644 --- a/python/src/com/jetbrains/python/testing/attest/PythonAtTestConfigurationProducer.java +++ b/python/src/com/jetbrains/python/testing/attest/PythonAtTestConfigurationProducer.java @@ -5,6 +5,8 @@ import com.intellij.execution.Location; import com.intellij.openapi.projectRoots.Sdk; import com.intellij.psi.PsiElement; import com.jetbrains.python.psi.*; +import com.jetbrains.python.psi.types.PyClassLikeType; +import com.jetbrains.python.psi.types.TypeEvalContext; import com.jetbrains.python.sdk.PythonSdkType; import com.jetbrains.python.testing.*; @@ -27,9 +29,10 @@ public class PythonAtTestConfigurationProducer extends protected boolean isTestClass(PyClass pyClass) { if (pyClass == null) return false; - for (PyClassRef an : pyClass.iterateAncestors()) { - if ("TestBase".equals(an.getClassName()) && hasTestFunction(pyClass)) + for (PyClassLikeType type : pyClass.getAncestorTypes(TypeEvalContext.fastStubOnly(null))) { + if (type != null && "TestBase".equals(type.getName()) && hasTestFunction(pyClass)) { return true; + } } return false; } diff --git a/python/src/com/jetbrains/python/testing/pytest/PyTestUtil.java b/python/src/com/jetbrains/python/testing/pytest/PyTestUtil.java index 9e7c242a817a..e32945a561e2 100644 --- a/python/src/com/jetbrains/python/testing/pytest/PyTestUtil.java +++ b/python/src/com/jetbrains/python/testing/pytest/PyTestUtil.java @@ -6,6 +6,8 @@ import com.intellij.psi.PsiDirectory; import com.intellij.psi.PsiFile; import com.intellij.psi.PsiFileSystemItem; import com.jetbrains.python.psi.*; +import com.jetbrains.python.psi.types.PyClassLikeType; +import com.jetbrains.python.psi.types.TypeEvalContext; import java.util.HashSet; import java.util.List; @@ -54,13 +56,11 @@ public class PyTestUtil { } public static boolean isPyTestClass(PyClass pyClass) { - for (PyClassRef ancestor : pyClass.iterateAncestors()) { - String qName = ancestor.getQualifiedName(); - if (PYTHON_TEST_QUALIFIED_CLASSES.contains(qName)) { + for (PyClassLikeType type : pyClass.getAncestorTypes(TypeEvalContext.fastStubOnly(null))) { + if (type != null && PYTHON_TEST_QUALIFIED_CLASSES.contains(type.getClassQName())) { return true; } } - final String className = pyClass.getName(); if (className == null) return false; final String name = className.toLowerCase(); diff --git a/python/testSrc/com/jetbrains/python/PyStubsTest.java b/python/testSrc/com/jetbrains/python/PyStubsTest.java index 850d4ffbbf60..e7c46c75a1c8 100644 --- a/python/testSrc/com/jetbrains/python/PyStubsTest.java +++ b/python/testSrc/com/jetbrains/python/PyStubsTest.java @@ -347,8 +347,8 @@ public class PyStubsTest extends PyTestCase { public void testBuiltinAncestor() { final PyFileImpl file = (PyFileImpl) getTestFile(); final PyClass pyClass = file.getTopLevelClasses().get(0); - final PyClassRef classRef = pyClass.iterateAncestors().iterator().next(); - assertNotNull(classRef.getPyClass()); + final PyClass cls = pyClass.iterateAncestorClasses().iterator().next(); + assertNotNull(cls); assertNotParsed(file); } From 563104c668766f4636dc96fbc6da14e87b66a3ab Mon Sep 17 00:00:00 2001 From: Andrey Vlasovskikh Date: Thu, 18 Apr 2013 15:45:21 +0400 Subject: [PATCH 10/17] Changed PyClass.iterateAncestorClasses() to getAncestorClasses() --- .../src/com/jetbrains/python/psi/PyClass.java | 3 ++- .../codeInsight/PyGotoSuperHandler.java | 4 ++-- .../codeInsight/PyLineMarkerProvider.java | 4 ++-- ...rClassAttributesCompletionContributor.java | 2 +- .../PySuperMethodCompletionContributor.java | 2 +- .../override/PyOverrideImplementUtil.java | 2 +- .../documentation/PyDocumentationBuilder.java | 2 +- .../PythonDocumentationProvider.java | 2 +- .../PyAttributeOutsideInitInspection.java | 2 +- .../PyClassHasNoInitInspection.java | 2 +- .../PyMissingConstructorInspection.java | 8 ++++---- .../PyUnresolvedReferencesInspection.java | 4 ++-- .../quickfix/AddFieldQuickFix.java | 2 +- .../src/com/jetbrains/python/psi/PyUtil.java | 6 +++--- .../psi/impl/PyCallExpressionHelper.java | 2 +- .../python/psi/impl/PyClassImpl.java | 19 ++++++++++--------- .../psi/impl/references/PyReferenceImpl.java | 2 +- .../psi/search/PySuperMethodsSearch.java | 2 +- .../search/PySuperMethodsSearchExecutor.java | 2 +- .../python/psi/types/PyClassTypeImpl.java | 2 +- .../structureView/PyStructureViewElement.java | 2 +- .../com/jetbrains/python/PyStubsTest.java | 2 +- 22 files changed, 40 insertions(+), 38 deletions(-) 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 9fc66ae306c3..26553ee50951 100644 --- a/python/psi-api/src/com/jetbrains/python/psi/PyClass.java +++ b/python/psi-api/src/com/jetbrains/python/psi/PyClass.java @@ -110,7 +110,8 @@ public interface PyClass extends PsiNameIdentifierOwner, PyStatement, NameDefine */ boolean isNewStyleClass(); - Iterable iterateAncestorClasses(); + @NotNull + List getAncestorClasses(); /** * Get a list of all ancestor types. diff --git a/python/src/com/jetbrains/python/codeInsight/PyGotoSuperHandler.java b/python/src/com/jetbrains/python/codeInsight/PyGotoSuperHandler.java index 772dce16c403..b6eeb274a937 100644 --- a/python/src/com/jetbrains/python/codeInsight/PyGotoSuperHandler.java +++ b/python/src/com/jetbrains/python/codeInsight/PyGotoSuperHandler.java @@ -70,7 +70,7 @@ public class PyGotoSuperHandler implements CodeInsightActionHandler { return Collections.emptyList(); } final List result = new ArrayList(); - for (PyClass aClass: pyClass.iterateAncestorClasses()) { + for (PyClass aClass: pyClass.getAncestorClasses()) { final PyTargetExpression superAttr = aClass.findClassAttribute(name, false); if (superAttr != null) { result.add(superAttr); @@ -85,7 +85,7 @@ public class PyGotoSuperHandler implements CodeInsightActionHandler { return Collections.emptyList(); } final List result = new ArrayList(); - for (PyClass aClass: pyClass.iterateAncestorClasses()) { + for (PyClass aClass: pyClass.getAncestorClasses()) { final PyFunction byName = aClass.findMethodByName(name, false); if (byName != null) { result.add(byName); diff --git a/python/src/com/jetbrains/python/codeInsight/PyLineMarkerProvider.java b/python/src/com/jetbrains/python/codeInsight/PyLineMarkerProvider.java index fad0bb078e4e..f382f616403d 100644 --- a/python/src/com/jetbrains/python/codeInsight/PyLineMarkerProvider.java +++ b/python/src/com/jetbrains/python/codeInsight/PyLineMarkerProvider.java @@ -108,7 +108,7 @@ public class PyLineMarkerProvider implements LineMarkerProvider, PyLineSeparator List result = new ArrayList(); PyClass containingClass = PsiTreeUtil.getParentOfType(elt, PyClass.class); if (containingClass != null && elt instanceof PyTargetExpression) { - for (PyClass ancestor : containingClass.iterateAncestorClasses()) { + for (PyClass ancestor : containingClass.getAncestorClasses()) { final PyTargetExpression attribute = ancestor.findClassAttribute(((PyTargetExpression)elt).getReferencedName(), false); if (attribute != null) { result.add(attribute); @@ -185,7 +185,7 @@ public class PyLineMarkerProvider implements LineMarkerProvider, PyLineSeparator } PyClass containingClass = PsiTreeUtil.getParentOfType(element, PyClass.class); if (containingClass == null) return null; - for (PyClass ancestor : containingClass.iterateAncestorClasses()) { + for (PyClass ancestor : containingClass.getAncestorClasses()) { final PyTargetExpression ancestorAttr = ancestor.findClassAttribute(name, false); if (ancestorAttr != null) { return new LineMarkerInfo(element, element.getTextRange().getStartOffset(), diff --git a/python/src/com/jetbrains/python/codeInsight/completion/PySuperClassAttributesCompletionContributor.java b/python/src/com/jetbrains/python/codeInsight/completion/PySuperClassAttributesCompletionContributor.java index 95a61ccd2197..20624e6f6e11 100644 --- a/python/src/com/jetbrains/python/codeInsight/completion/PySuperClassAttributesCompletionContributor.java +++ b/python/src/com/jetbrains/python/codeInsight/completion/PySuperClassAttributesCompletionContributor.java @@ -45,7 +45,7 @@ public class PySuperClassAttributesCompletionContributor extends CompletionContr for (PyTargetExpression expr : cls.getClassAttributes()) { seenNames.add(expr.getName()); } - for (PyClass ancestor : cls.iterateAncestorClasses()) { + for (PyClass ancestor : cls.getAncestorClasses()) { for (PyTargetExpression expr : ancestor.getClassAttributes()) { if (!seenNames.contains(expr.getName())) { seenNames.add(expr.getName()); diff --git a/python/src/com/jetbrains/python/codeInsight/completion/PySuperMethodCompletionContributor.java b/python/src/com/jetbrains/python/codeInsight/completion/PySuperMethodCompletionContributor.java index 197220f10607..9808b146196e 100644 --- a/python/src/com/jetbrains/python/codeInsight/completion/PySuperMethodCompletionContributor.java +++ b/python/src/com/jetbrains/python/codeInsight/completion/PySuperMethodCompletionContributor.java @@ -47,7 +47,7 @@ public class PySuperMethodCompletionContributor extends CompletionContributor { } LanguageLevel languageLevel = LanguageLevel.forElement(parameters.getOriginalFile()); seenNames.addAll(PyNames.getBuiltinMethods(languageLevel).keySet()); - for (PyClass ancestor : containingClass.iterateAncestorClasses()) { + for (PyClass ancestor : containingClass.getAncestorClasses()) { for (PyFunction superMethod : ancestor.getMethods()) { if (!seenNames.contains(superMethod.getName())) { String text = superMethod.getName() + superMethod.getParameterList().getText(); diff --git a/python/src/com/jetbrains/python/codeInsight/override/PyOverrideImplementUtil.java b/python/src/com/jetbrains/python/codeInsight/override/PyOverrideImplementUtil.java index 6f5d5c9f0edf..29b20be5bab5 100644 --- a/python/src/com/jetbrains/python/codeInsight/override/PyOverrideImplementUtil.java +++ b/python/src/com/jetbrains/python/codeInsight/override/PyOverrideImplementUtil.java @@ -239,7 +239,7 @@ public class PyOverrideImplementUtil { @NotNull private static Collection getAllSuperFunctions(@NotNull final PyClass pyClass) { final Map superFunctions = new HashMap(); - for (PyClass aClass : pyClass.iterateAncestorClasses()) { + for (PyClass aClass : pyClass.getAncestorClasses()) { for (PyFunction function : aClass.getMethods()) { if (!superFunctions.containsKey(function.getName())) { superFunctions.put(function.getName(), function); diff --git a/python/src/com/jetbrains/python/documentation/PyDocumentationBuilder.java b/python/src/com/jetbrains/python/documentation/PyDocumentationBuilder.java index 5bbee185483f..e0c38c0fc73c 100644 --- a/python/src/com/jetbrains/python/documentation/PyDocumentationBuilder.java +++ b/python/src/com/jetbrains/python/documentation/PyDocumentationBuilder.java @@ -286,7 +286,7 @@ class PyDocumentationBuilder { if (cls != null && meth_name != null) { final boolean is_constructor = PyNames.INIT.equals(meth_name); // look for inherited and its doc - Iterable classes = cls.iterateAncestorClasses(); + Iterable classes = cls.getAncestorClasses(); if (is_constructor) { // look at our own class again and maybe inherit class's doc classes = new ChainIterable(cls).add(classes); diff --git a/python/src/com/jetbrains/python/documentation/PythonDocumentationProvider.java b/python/src/com/jetbrains/python/documentation/PythonDocumentationProvider.java index 6d27691521bc..33e328389e26 100644 --- a/python/src/com/jetbrains/python/documentation/PythonDocumentationProvider.java +++ b/python/src/com/jetbrains/python/documentation/PythonDocumentationProvider.java @@ -313,7 +313,7 @@ public class PythonDocumentationProvider extends AbstractDocumentationProvider i PyClass cls = inferContainingClassOf(context); if (cls != null) { String desired_name = link.substring(LINK_TYPE_PARENT.length()); - for (PyClass parent : cls.iterateAncestorClasses()) { + for (PyClass parent : cls.getAncestorClasses()) { final String parent_name = parent.getName(); if (parent_name != null && parent_name.equals(desired_name)) return parent; } diff --git a/python/src/com/jetbrains/python/inspections/PyAttributeOutsideInitInspection.java b/python/src/com/jetbrains/python/inspections/PyAttributeOutsideInitInspection.java index a615a13b15c5..02cdf65cc08e 100644 --- a/python/src/com/jetbrains/python/inspections/PyAttributeOutsideInitInspection.java +++ b/python/src/com/jetbrains/python/inspections/PyAttributeOutsideInitInspection.java @@ -65,7 +65,7 @@ public class PyAttributeOutsideInitInspection extends PyInspection { collectAttributesFromSuper(attributesInInit, initMethod); } else { - for (PyClass superClass : containingClass.iterateAncestorClasses()) { + for (PyClass superClass : containingClass.getAncestorClasses()) { final PyFunction superInit = superClass.findMethodByName(PyNames.INIT, false); if (superInit != null) PyClassImpl.collectInstanceAttributes(superInit, attributesInInit); diff --git a/python/src/com/jetbrains/python/inspections/PyClassHasNoInitInspection.java b/python/src/com/jetbrains/python/inspections/PyClassHasNoInitInspection.java index 04c623dcac4c..82df134a19b1 100644 --- a/python/src/com/jetbrains/python/inspections/PyClassHasNoInitInspection.java +++ b/python/src/com/jetbrains/python/inspections/PyClassHasNoInitInspection.java @@ -44,7 +44,7 @@ public class PyClassHasNoInitInspection extends PyInspection { registerProblem(node.getNameIdentifier(), PyBundle.message("INSP.class.has.no.init"), new AddMethodQuickFix("__init__", new PyClassTypeImpl(node, false), false)); } - for (PyClass ancestor : node.iterateAncestorClasses()) { + for (PyClass ancestor : node.getAncestorClasses()) { final PyFunction ancestorInit = ancestor.findInitOrNew(false); if (ancestorInit == null) { registerProblem(node.getNameIdentifier(), PyBundle.message("INSP.parent.$0.has.no.init", ancestor.getName()), diff --git a/python/src/com/jetbrains/python/inspections/PyMissingConstructorInspection.java b/python/src/com/jetbrains/python/inspections/PyMissingConstructorInspection.java index 6f3d6d119e30..6ce1ab531f45 100644 --- a/python/src/com/jetbrains/python/inspections/PyMissingConstructorInspection.java +++ b/python/src/com/jetbrains/python/inspections/PyMissingConstructorInspection.java @@ -62,7 +62,7 @@ public class PyMissingConstructorInspection extends PyInspection { } private static boolean superHasConstructor(@NotNull PyClass cls) { - for (PyClass c : cls.iterateAncestorClasses()) { + for (PyClass c : cls.getAncestorClasses()) { final String name = c.getName(); final String className = cls.getName(); if (!OBJECT.equals(name) && !FAKE_OLD_BASE.equals(name) && className != null && @@ -77,7 +77,7 @@ public class PyMissingConstructorInspection extends PyInspection { if (PyBroadExceptionInspection.equalsException(cls, context)) { return true; } - for (PyClass baseClass : cls.iterateAncestorClasses()) { + for (PyClass baseClass : cls.getAncestorClasses()) { if (PyBroadExceptionInspection.equalsException(baseClass, context)) { return true; } @@ -127,7 +127,7 @@ public class PyMissingConstructorInspection extends PyInspection { if (firstArg.equals(cl.getName()) || firstArg.equals(CANONICAL_SELF+"."+ CLASS) || (qualifiedName != null && qualifiedName.endsWith(firstArg))) return true; - for (PyClass s : cl.iterateAncestorClasses()) { + for (PyClass s : cl.getAncestorClasses()) { if (firstArg.equals(s.getName())) return true; } @@ -159,7 +159,7 @@ public class PyMissingConstructorInspection extends PyInspection { if (ref != null) callingClass = ref.resolve(); } - for (PyClass s : cl.iterateAncestorClasses()) { + for (PyClass s : cl.getAncestorClasses()) { if (s.equals(callingClass)) { return true; } diff --git a/python/src/com/jetbrains/python/inspections/PyUnresolvedReferencesInspection.java b/python/src/com/jetbrains/python/inspections/PyUnresolvedReferencesInspection.java index 3d694e7a1066..37e76f0108bd 100644 --- a/python/src/com/jetbrains/python/inspections/PyUnresolvedReferencesInspection.java +++ b/python/src/com/jetbrains/python/inspections/PyUnresolvedReferencesInspection.java @@ -134,7 +134,7 @@ public class PyUnresolvedReferencesInspection extends PyInspection { final List slots = pyClass.getSlots(); final String attrName = node.getReferencedName(); if (slots != null && !slots.contains(attrName) && !slots.contains(PyNames.DICT)) { - for (PyClass ancestor : pyClass.iterateAncestorClasses()) { + for (PyClass ancestor : pyClass.getAncestorClasses()) { if (ancestor == null) { return; } @@ -672,7 +672,7 @@ public class PyUnresolvedReferencesInspection extends PyInspection { if (isDecoratedAsDynamic(cls, false)) { return true; } - for (PyClass base : cls.iterateAncestorClasses()) { + for (PyClass base : cls.getAncestorClasses()) { if (base != null && isDecoratedAsDynamic(base, false)) { return true; } diff --git a/python/src/com/jetbrains/python/inspections/quickfix/AddFieldQuickFix.java b/python/src/com/jetbrains/python/inspections/quickfix/AddFieldQuickFix.java index de8185679e81..af6b517239c3 100644 --- a/python/src/com/jetbrains/python/inspections/quickfix/AddFieldQuickFix.java +++ b/python/src/com/jetbrains/python/inspections/quickfix/AddFieldQuickFix.java @@ -105,7 +105,7 @@ public class AddFieldQuickFix implements LocalQuickFix { return appendToMethod(init, callback); } else { // no init! boldly copy ancestor's. - for (PyClass ancestor : cls.iterateAncestorClasses()) { + for (PyClass ancestor : cls.getAncestorClasses()) { init = ancestor.findMethodByName(PyNames.INIT, false); if (init != null) break; } diff --git a/python/src/com/jetbrains/python/psi/PyUtil.java b/python/src/com/jetbrains/python/psi/PyUtil.java index 528ff6a5151a..15e7d456a6d7 100644 --- a/python/src/com/jetbrains/python/psi/PyUtil.java +++ b/python/src/com/jetbrains/python/psi/PyUtil.java @@ -375,7 +375,7 @@ public class PyUtil { @NotNull public static List getAllSuperClasses(@NotNull PyClass pyClass) { List superClasses = new ArrayList(); - for (PyClass ancestor : pyClass.iterateAncestorClasses()) { + for (PyClass ancestor : pyClass.getAncestorClasses()) { if (!PyNames.FAKE_OLD_BASE.equals(ancestor.getName())) { superClasses.add(ancestor); } @@ -1018,7 +1018,7 @@ public class PyUtil { PyFunction.Modifier modifier = node.getModifier(); boolean isMetaclassMethod = false; PyClass type_cls = PyBuiltinCache.getInstance(node).getClass("type"); - for (PyClass ancestor_cls : cls.iterateAncestorClasses()) { + for (PyClass ancestor_cls : cls.getAncestorClasses()) { if (ancestor_cls == type_cls) { isMetaclassMethod = true; break; @@ -1050,7 +1050,7 @@ public class PyUtil { if (firstArg.equals(klass.getName()) || firstArg.equals(PyNames.CANONICAL_SELF + "." + PyNames.CLASS)) { return true; } - for (PyClass s : klass.iterateAncestorClasses()) { + for (PyClass s : klass.getAncestorClasses()) { if (firstArg.equals(s.getName())) { return true; } diff --git a/python/src/com/jetbrains/python/psi/impl/PyCallExpressionHelper.java b/python/src/com/jetbrains/python/psi/impl/PyCallExpressionHelper.java index 844a41a1576a..9c78ef8a688c 100644 --- a/python/src/com/jetbrains/python/psi/impl/PyCallExpressionHelper.java +++ b/python/src/com/jetbrains/python/psi/impl/PyCallExpressionHelper.java @@ -505,7 +505,7 @@ public class PyCallExpressionHelper { return getSuperClassUnionType(firstClass); } if (secondClass.isSubclass(firstClass)) { - final Iterator iterator = firstClass.iterateAncestorClasses().iterator(); + final Iterator iterator = firstClass.getAncestorClasses().iterator(); if (iterator.hasNext()) { return new PyClassTypeImpl(iterator.next(), false); // super(Foo, self) has type of Foo, modulo __get__() } diff --git a/python/src/com/jetbrains/python/psi/impl/PyClassImpl.java b/python/src/com/jetbrains/python/psi/impl/PyClassImpl.java index 02ccb6b82831..a9881353f251 100644 --- a/python/src/com/jetbrains/python/psi/impl/PyClassImpl.java +++ b/python/src/com/jetbrains/python/psi/impl/PyClassImpl.java @@ -174,8 +174,9 @@ public class PyClassImpl extends PyPresentableElementImpl implement return expression; } + @NotNull @Override - public Iterable iterateAncestorClasses() { + public List getAncestorClasses() { final List results = new ArrayList(); for (PyClassLikeType type : getAncestorTypes(TypeEvalContext.fastStubOnly(null))) { if (type instanceof PyClassType) { @@ -189,7 +190,7 @@ public class PyClassImpl extends PyPresentableElementImpl implement if (this == parent) { return true; } - for (PyClass superclass : iterateAncestorClasses()) { + for (PyClass superclass : getAncestorClasses()) { if (parent == superclass) return true; } return false; @@ -243,7 +244,7 @@ public class PyClassImpl extends PyPresentableElementImpl implement if (slots != null) { return slots; } - for (PyClass cls : iterateAncestorClasses()) { + for (PyClass cls : getAncestorClasses()) { slots = ((PyClassImpl)cls).getOwnSlots(); if (slots != null) { return slots; @@ -549,7 +550,7 @@ public class PyClassImpl extends PyPresentableElementImpl implement if (findMethodByName(name, false) != null || findClassAttribute(name, false) != null) { return null; } - for (PyClass aClass : iterateAncestorClasses()) { + for (PyClass aClass : getAncestorClasses()) { final Property ancestorProperty = ((PyClassImpl)aClass).findLocalProperty(name); if (ancestorProperty != null) { return ancestorProperty; @@ -621,7 +622,7 @@ public class PyClassImpl extends PyPresentableElementImpl implement if (name != null && (findMethodByName(name, false) != null || findClassAttribute(name, false) != null)) { return null; } - for (PyClass cls : iterateAncestorClasses()) { + for (PyClass cls : getAncestorClasses()) { final Property property = ((PyClassImpl)cls).processPropertiesInClass(name, filter, useAdvancedSyntax); if (property != null) { return property; @@ -713,7 +714,7 @@ public class PyClassImpl extends PyPresentableElementImpl implement PyFunction[] methods = getMethods(); if (!ContainerUtil.process(methods, processor)) return false; if (inherited) { - for (PyClass ancestor : iterateAncestorClasses()) { + for (PyClass ancestor : getAncestorClasses()) { if (skipClassObj && PyNames.FAKE_OLD_BASE.equals(ancestor.getName())) { continue; } @@ -729,7 +730,7 @@ public class PyClassImpl extends PyPresentableElementImpl implement PyClass[] nestedClasses = getNestedClasses(); if (!ContainerUtil.process(nestedClasses, processor)) return false; if (inherited) { - for (PyClass ancestor : iterateAncestorClasses()) { + for (PyClass ancestor : getAncestorClasses()) { if (!((PyClassImpl)ancestor).visitNestedClasses(processor, false)) { return false; } @@ -742,7 +743,7 @@ public class PyClassImpl extends PyPresentableElementImpl implement List methods = getClassAttributes(); if (!ContainerUtil.process(methods, processor)) return false; if (inherited) { - for (PyClass ancestor : iterateAncestorClasses()) { + for (PyClass ancestor : getAncestorClasses()) { if (!ancestor.visitClassAttributes(processor, false)) { return false; } @@ -797,7 +798,7 @@ public class PyClassImpl extends PyPresentableElementImpl implement } } if (inherited) { - for (PyClass ancestor : iterateAncestorClasses()) { + for (PyClass ancestor : getAncestorClasses()) { final PyTargetExpression attribute = ancestor.findInstanceAttribute(name, false); if (attribute != null) { return attribute; diff --git a/python/src/com/jetbrains/python/psi/impl/references/PyReferenceImpl.java b/python/src/com/jetbrains/python/psi/impl/references/PyReferenceImpl.java index 1d5fd6ddc87a..14184a573b4f 100644 --- a/python/src/com/jetbrains/python/psi/impl/references/PyReferenceImpl.java +++ b/python/src/com/jetbrains/python/psi/impl/references/PyReferenceImpl.java @@ -125,7 +125,7 @@ public class PyReferenceImpl implements PsiReferenceEx, PsiPolyVariantReference it.set(rrr.replace(init)); } else { // init not found; maybe it's ancestor's - for (PyClass ancestor : cls.iterateAncestorClasses()) { + for (PyClass ancestor : cls.getAncestorClasses()) { init = ancestor.findMethodByName(PyNames.INIT, false); if (init != null) { // add to results as low priority diff --git a/python/src/com/jetbrains/python/psi/search/PySuperMethodsSearch.java b/python/src/com/jetbrains/python/psi/search/PySuperMethodsSearch.java index 1186ba2499ee..895e7f08c86a 100644 --- a/python/src/com/jetbrains/python/psi/search/PySuperMethodsSearch.java +++ b/python/src/com/jetbrains/python/psi/search/PySuperMethodsSearch.java @@ -18,7 +18,7 @@ public class PySuperMethodsSearch extends ExtensibleQueryFactory superMethods, PyClass containingClass) { - for (PyClass ancestor : containingClass.iterateAncestorClasses()) { + for (PyClass ancestor : containingClass.getAncestorClasses()) { for (PsiElement method : superMethods) { if (ancestor.equals(((PyFunction)method).getContainingClass())) return (PyFunction)method; diff --git a/python/src/com/jetbrains/python/psi/search/PySuperMethodsSearchExecutor.java b/python/src/com/jetbrains/python/psi/search/PySuperMethodsSearchExecutor.java index 81a6318a383d..1a1afe58d570 100644 --- a/python/src/com/jetbrains/python/psi/search/PySuperMethodsSearchExecutor.java +++ b/python/src/com/jetbrains/python/psi/search/PySuperMethodsSearchExecutor.java @@ -19,7 +19,7 @@ public class PySuperMethodsSearchExecutor implements QueryExecutor foundMethodContainingClasses = new HashSet(); if (name != null && containingClass != null) { - for (PyClass superClass : containingClass.iterateAncestorClasses()) { + for (PyClass superClass : containingClass.getAncestorClasses()) { if (!queryParameters.isDeepSearch()) { boolean isAlreadyFound = false; for (PyClass alreadyFound : foundMethodContainingClasses) { diff --git a/python/src/com/jetbrains/python/psi/types/PyClassTypeImpl.java b/python/src/com/jetbrains/python/psi/types/PyClassTypeImpl.java index 1974fe5e4ad7..0b24f8100746 100644 --- a/python/src/com/jetbrains/python/psi/types/PyClassTypeImpl.java +++ b/python/src/com/jetbrains/python/psi/types/PyClassTypeImpl.java @@ -155,7 +155,7 @@ public class PyClassTypeImpl extends UserDataHolderBase implements PyClassType { PyType first_arg_type = context.getType(first_arg); if (first_arg_type instanceof PyClassType) { PyClass derived_class = ((PyClassType)first_arg_type).getPyClass(); - final Iterator base_it = derived_class.iterateAncestorClasses().iterator(); + final Iterator base_it = derived_class.getAncestorClasses().iterator(); if (base_it.hasNext()) { return new PyClassTypeImpl(base_it.next(), true).resolveMember(name, location, direction, resolveContext); } diff --git a/python/src/com/jetbrains/python/structureView/PyStructureViewElement.java b/python/src/com/jetbrains/python/structureView/PyStructureViewElement.java index a976ada169d7..ca3dba6c5657 100644 --- a/python/src/com/jetbrains/python/structureView/PyStructureViewElement.java +++ b/python/src/com/jetbrains/python/structureView/PyStructureViewElement.java @@ -111,7 +111,7 @@ public class PyStructureViewElement implements StructureViewTreeElement { children.add(createChild(e, getElementVisibility(e), false, elementIsField(e))); } if (myElement instanceof PyClass && myElement.isValid()) { - for (PyClass c : ((PyClass)myElement).iterateAncestorClasses()) { + for (PyClass c : ((PyClass)myElement).getAncestorClasses()) { for (PyElement e: getElementChildren(c)) { final StructureViewTreeElement inherited = createChild(e, getElementVisibility(e), true, elementIsField(e)); if (!children.contains(inherited)) { diff --git a/python/testSrc/com/jetbrains/python/PyStubsTest.java b/python/testSrc/com/jetbrains/python/PyStubsTest.java index e7c46c75a1c8..11d6714025d7 100644 --- a/python/testSrc/com/jetbrains/python/PyStubsTest.java +++ b/python/testSrc/com/jetbrains/python/PyStubsTest.java @@ -347,7 +347,7 @@ public class PyStubsTest extends PyTestCase { public void testBuiltinAncestor() { final PyFileImpl file = (PyFileImpl) getTestFile(); final PyClass pyClass = file.getTopLevelClasses().get(0); - final PyClass cls = pyClass.iterateAncestorClasses().iterator().next(); + final PyClass cls = pyClass.getAncestorClasses().iterator().next(); assertNotNull(cls); assertNotParsed(file); } From cfc8d927bc3c2ac818802c3958da99e9b826480f Mon Sep 17 00:00:00 2001 From: Andrey Vlasovskikh Date: Thu, 18 Apr 2013 15:49:52 +0400 Subject: [PATCH 11/17] Added PyClass.getAncestorClasses(TypeEvalContext) --- python/psi-api/src/com/jetbrains/python/psi/PyClass.java | 3 +++ python/src/com/jetbrains/python/psi/impl/PyClassImpl.java | 8 +++++++- 2 files changed, 10 insertions(+), 1 deletion(-) 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 26553ee50951..c5054bee8cee 100644 --- a/python/psi-api/src/com/jetbrains/python/psi/PyClass.java +++ b/python/psi-api/src/com/jetbrains/python/psi/PyClass.java @@ -113,6 +113,9 @@ public interface PyClass extends PsiNameIdentifierOwner, PyStatement, NameDefine @NotNull List getAncestorClasses(); + @NotNull + List getAncestorClasses(@NotNull TypeEvalContext context); + /** * Get a list of all ancestor types. */ diff --git a/python/src/com/jetbrains/python/psi/impl/PyClassImpl.java b/python/src/com/jetbrains/python/psi/impl/PyClassImpl.java index a9881353f251..1f0243284ebe 100644 --- a/python/src/com/jetbrains/python/psi/impl/PyClassImpl.java +++ b/python/src/com/jetbrains/python/psi/impl/PyClassImpl.java @@ -177,8 +177,14 @@ public class PyClassImpl extends PyPresentableElementImpl implement @NotNull @Override public List getAncestorClasses() { + return getAncestorClasses(TypeEvalContext.fastStubOnly(null)); + } + + @NotNull + @Override + public List getAncestorClasses(@NotNull TypeEvalContext context) { final List results = new ArrayList(); - for (PyClassLikeType type : getAncestorTypes(TypeEvalContext.fastStubOnly(null))) { + for (PyClassLikeType type : getAncestorTypes(context)) { if (type instanceof PyClassType) { results.add(((PyClassType)type).getPyClass()); } From f8eb0dd7f61a590a3fd2ec6998e446d111176903 Mon Sep 17 00:00:00 2001 From: Andrey Vlasovskikh Date: Thu, 18 Apr 2013 18:05:03 +0400 Subject: [PATCH 12/17] Fixed resolve of multiple inherited constructors for new-style MRO (PY-9080) Integrated a C3 MRO algorithm implementation into PyClass.getAncestorTypes(). --- .../src/com/jetbrains/python/psi/PyClass.java | 11 --- .../python/psi/impl/PyClassImpl.java | 76 +++++++++++-------- .../multipleInheritedConstructorsMRO.py | 18 +++++ .../PyArgumentListInspectionTest.java | 5 ++ 4 files changed, 67 insertions(+), 43 deletions(-) create mode 100644 python/testData/inspections/PyArgumentListInspection/multipleInheritedConstructorsMRO.py 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 { From d6972abf2ad0db941f03ca5c5c143236122dce34 Mon Sep 17 00:00:00 2001 From: Andrey Vlasovskikh Date: Thu, 18 Apr 2013 21:03:50 +0400 Subject: [PATCH 13/17] Fixed several bugs in new-style MRO algorithm --- .../python/psi/impl/PyClassImpl.java | 42 ++++++---- .../classMRO/CircularInheritance.py | 5 ++ .../classMRO/CircularInheritance2.py | 9 +++ .../codeInsight/classMRO/ExampleFromDoc1.py | 22 ++++++ .../codeInsight/classMRO/ExampleFromDoc2.py | 22 ++++++ .../codeInsight/classMRO/ExampleFromDoc3.py | 10 +++ .../codeInsight/classMRO/ExampleFromDoc4.py | 10 +++ .../codeInsight/classMRO/MROConflict.py | 18 +++++ .../codeInsight/classMRO/SimpleDiamond.py | 10 +++ .../jetbrains/python/PythonAllTestsSuite.java | 2 + .../python/codeInsight/PyClassMROTest.java | 77 +++++++++++++++++++ 11 files changed, 212 insertions(+), 15 deletions(-) create mode 100644 python/testData/codeInsight/classMRO/CircularInheritance.py create mode 100644 python/testData/codeInsight/classMRO/CircularInheritance2.py create mode 100644 python/testData/codeInsight/classMRO/ExampleFromDoc1.py create mode 100644 python/testData/codeInsight/classMRO/ExampleFromDoc2.py create mode 100644 python/testData/codeInsight/classMRO/ExampleFromDoc3.py create mode 100644 python/testData/codeInsight/classMRO/ExampleFromDoc4.py create mode 100644 python/testData/codeInsight/classMRO/MROConflict.py create mode 100644 python/testData/codeInsight/classMRO/SimpleDiamond.py create mode 100644 python/testSrc/com/jetbrains/python/codeInsight/PyClassMROTest.java diff --git a/python/src/com/jetbrains/python/psi/impl/PyClassImpl.java b/python/src/com/jetbrains/python/psi/impl/PyClassImpl.java index 9c51e1a04a0e..f5079648c79f 100644 --- a/python/src/com/jetbrains/python/psi/impl/PyClassImpl.java +++ b/python/src/com/jetbrains/python/psi/impl/PyClassImpl.java @@ -14,7 +14,7 @@ import com.intellij.psi.stubs.StubElement; import com.intellij.psi.tree.TokenSet; import com.intellij.psi.util.*; import com.intellij.util.*; -import com.intellij.util.containers.*; +import com.intellij.util.containers.ContainerUtil; import com.jetbrains.python.*; import com.jetbrains.python.codeInsight.controlflow.ControlFlowCache; import com.jetbrains.python.codeInsight.controlflow.ScopeOwner; @@ -34,8 +34,6 @@ import org.jetbrains.annotations.Nullable; import javax.swing.*; import java.util.*; -import java.util.HashMap; -import java.util.HashSet; /** * @author yole @@ -295,6 +293,7 @@ public class PyClassImpl extends PyPresentableElementImpl implement } if (nonBlankSequences.isEmpty()) return result; // find a clean head + boolean found = false; 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); @@ -306,39 +305,48 @@ public class PyClassImpl extends PyPresentableElementImpl implement } } if (!head_in_tails) { + found = true; break; } else { head = null; // as a signal } } + if (!found) { + // Inconsistent hierarchy results in TypeError + throw new IllegalStateException("Inconsistent class hierarchy"); + } // our head is clean; result.add(head); // remove it from heads of other sequences for (List seq : nonBlankSequences) { - if (Comparing.equal(seq.get(0), head)) seq.remove(0); + if (Comparing.equal(seq.get(0), head)) { + seq.remove(0); + } } } // we either return inside the loop or die by assertion } @NotNull - private static List mroLinearize(@NotNull PyClassLikeType type, @NotNull List seen, boolean addThisType, + private static List mroLinearize(@NotNull PyClassLikeType type, @NotNull Set seen, boolean addThisType, @NotNull TypeEvalContext context) { - assert (seen.indexOf(type) < 0) : "Circular import structure on " + PyUtil.nvl(type); + if (seen.contains(type)) { + throw new IllegalStateException("Circular class inheritance"); + } 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); + List> lines = new ArrayList>(); for (PyClassLikeType base : bases) { if (base != null) { - List lin = mroLinearize(base, new_seen, true, context); - if (!lin.isEmpty()) lins.add(lin); + final Set newSeen = new HashSet(seen); + newSeen.add(type); + List lin = mroLinearize(base, newSeen, true, context); + if (!lin.isEmpty()) lines.add(lin); } } - for (PyClassLikeType base : bases) { - lins.add(new SmartList(base)); + if (!bases.isEmpty()) { + lines.add(bases); } - List result = mroMerge(lins); + List result = mroMerge(lines); if (addThisType) { result.add(0, type); } @@ -1062,7 +1070,11 @@ public class PyClassImpl extends PyPresentableElementImpl implement private List getMROAncestorTypes(@NotNull TypeEvalContext context) { final PyType thisType = context.getType(this); if (thisType instanceof PyClassLikeType) { - return mroLinearize((PyClassLikeType)thisType, Collections.emptyList(), false, context); + try { + return mroLinearize((PyClassLikeType)thisType, new HashSet(), false, context); + } + catch (IllegalStateException ignored) { + } } return Collections.emptyList(); } diff --git a/python/testData/codeInsight/classMRO/CircularInheritance.py b/python/testData/codeInsight/classMRO/CircularInheritance.py new file mode 100644 index 000000000000..b268c4bf8432 --- /dev/null +++ b/python/testData/codeInsight/classMRO/CircularInheritance.py @@ -0,0 +1,5 @@ +from CircularInheritance2 import B + + +class Foo(B): + pass diff --git a/python/testData/codeInsight/classMRO/CircularInheritance2.py b/python/testData/codeInsight/classMRO/CircularInheritance2.py new file mode 100644 index 000000000000..de400f418664 --- /dev/null +++ b/python/testData/codeInsight/classMRO/CircularInheritance2.py @@ -0,0 +1,9 @@ +from CircularInheritance import Foo + + +class X(object): + pass + + +class B(X, Foo): + pass diff --git a/python/testData/codeInsight/classMRO/ExampleFromDoc1.py b/python/testData/codeInsight/classMRO/ExampleFromDoc1.py new file mode 100644 index 000000000000..d88d9ebd1674 --- /dev/null +++ b/python/testData/codeInsight/classMRO/ExampleFromDoc1.py @@ -0,0 +1,22 @@ +class D(object): + pass + + +class E(object): + pass + + +class F(object): + pass + + +class B(D, E): + pass + + +class C(D, F): + pass + + +class A(B, C): + pass diff --git a/python/testData/codeInsight/classMRO/ExampleFromDoc2.py b/python/testData/codeInsight/classMRO/ExampleFromDoc2.py new file mode 100644 index 000000000000..c1a8739bb3e4 --- /dev/null +++ b/python/testData/codeInsight/classMRO/ExampleFromDoc2.py @@ -0,0 +1,22 @@ +class D(object): + pass + + +class E(object): + pass + + +class F(object): + pass + + +class B(E, D): + pass + + +class C(D, F): + pass + + +class A(B, C): + pass diff --git a/python/testData/codeInsight/classMRO/ExampleFromDoc3.py b/python/testData/codeInsight/classMRO/ExampleFromDoc3.py new file mode 100644 index 000000000000..b650b29541d3 --- /dev/null +++ b/python/testData/codeInsight/classMRO/ExampleFromDoc3.py @@ -0,0 +1,10 @@ +class F(object): + pass + + +class E(F): + pass + + +class G(F, E): + pass diff --git a/python/testData/codeInsight/classMRO/ExampleFromDoc4.py b/python/testData/codeInsight/classMRO/ExampleFromDoc4.py new file mode 100644 index 000000000000..f996dadacec1 --- /dev/null +++ b/python/testData/codeInsight/classMRO/ExampleFromDoc4.py @@ -0,0 +1,10 @@ +class F(object): + pass + + +class E(F): + pass + + +class G(E, F): + pass diff --git a/python/testData/codeInsight/classMRO/MROConflict.py b/python/testData/codeInsight/classMRO/MROConflict.py new file mode 100644 index 000000000000..25e91a177f98 --- /dev/null +++ b/python/testData/codeInsight/classMRO/MROConflict.py @@ -0,0 +1,18 @@ +class X(object): + pass + + +class Y(object): + pass + + +class A(X, Y): + pass + + +class B(Y, X): + pass + + +class C(A, B): + pass \ No newline at end of file diff --git a/python/testData/codeInsight/classMRO/SimpleDiamond.py b/python/testData/codeInsight/classMRO/SimpleDiamond.py new file mode 100644 index 000000000000..69a5d0d92d9d --- /dev/null +++ b/python/testData/codeInsight/classMRO/SimpleDiamond.py @@ -0,0 +1,10 @@ +class B1(object): + pass + + +class B2(object): + pass + + +class C(B1, B2): + pass diff --git a/python/testSrc/com/jetbrains/python/PythonAllTestsSuite.java b/python/testSrc/com/jetbrains/python/PythonAllTestsSuite.java index d781f7a5c62a..42e3f59ddf72 100644 --- a/python/testSrc/com/jetbrains/python/PythonAllTestsSuite.java +++ b/python/testSrc/com/jetbrains/python/PythonAllTestsSuite.java @@ -3,6 +3,7 @@ package com.jetbrains.python; import com.jetbrains.cython.*; import com.jetbrains.django.lang.template.DjangoTemplateParserTest; import com.jetbrains.jinja2.Jinja2ParserTest; +import com.jetbrains.python.codeInsight.PyClassMROTest; import com.jetbrains.python.codeInsight.PyCompletionPatternsTest; import com.jetbrains.python.inspections.*; import com.jetbrains.python.refactoring.*; @@ -36,6 +37,7 @@ public class PythonAllTestsSuite { PyResolveTest.class, Py3ResolveTest.class, PyMultiFileResolveTest.class, + PyClassMROTest.class, PyResolveCalleeTest.class, CythonResolveTest.class, PyAssignmentMappingTest.class, diff --git a/python/testSrc/com/jetbrains/python/codeInsight/PyClassMROTest.java b/python/testSrc/com/jetbrains/python/codeInsight/PyClassMROTest.java new file mode 100644 index 000000000000..48904fd4e3bf --- /dev/null +++ b/python/testSrc/com/jetbrains/python/codeInsight/PyClassMROTest.java @@ -0,0 +1,77 @@ +package com.jetbrains.python.codeInsight; + +import com.jetbrains.python.fixtures.PyTestCase; +import com.jetbrains.python.psi.PyClass; +import com.jetbrains.python.psi.types.PyClassLikeType; +import com.jetbrains.python.psi.types.TypeEvalContext; +import org.jetbrains.annotations.NotNull; + +import java.util.ArrayList; +import java.util.Arrays; +import java.util.List; + +/** + * @author vlan + */ +public class PyClassMROTest extends PyTestCase { + public void testSimpleDiamond() { + assertMRO(getClass("C"), "B1", "B2", "object"); + } + + // TypeError in Python + public void testMROConflict() { + assertMRO(getClass("C")); + } + + public void testCircularInheritance() { + final String testName = getTestName(false); + myFixture.configureByFiles(getPath(testName), getPath(testName + "2")); + final PyClass cls = myFixture.findElementByText("Foo", PyClass.class); + assertNotNull(cls); + assertMRO(cls); + } + + public void testExampleFromDoc1() { + assertMRO(getClass("A"), "B", "C", "D", "E", "F", "object"); + } + + public void testExampleFromDoc2() { + assertMRO(getClass("A"), "B", "E", "C", "D", "F", "object"); + } + + public void testExampleFromDoc3() { + assertMRO(getClass("G")); + } + + public void testExampleFromDoc4() { + assertMRO(getClass("G"), "E", "F", "object"); + } + + public void assertMRO(@NotNull PyClass cls, @NotNull String... mro) { + final List types = cls.getAncestorTypes(TypeEvalContext.fastStubOnly(null)); + final List classNames = new ArrayList(); + for (PyClassLikeType type : types) { + if (type != null) { + final String name = type.getName(); + if (name != null) { + classNames.add(name); + continue; + } + } + classNames.add("unknown"); + } + assertOrderedEquals(classNames, Arrays.asList(mro)); + } + + @NotNull + public PyClass getClass(@NotNull String name) { + myFixture.configureByFile(getPath(getTestName(false))); + final PyClass cls = myFixture.findElementByText(name, PyClass.class); + assertNotNull(cls); + return cls; + } + + private static String getPath(String name) { + return "codeInsight/classMRO/" + name + ".py"; + } +} From e2f2bc616f658e6540673de0dd5814d5ee057732 Mon Sep 17 00:00:00 2001 From: Andrey Vlasovskikh Date: Thu, 18 Apr 2013 21:16:44 +0400 Subject: [PATCH 14/17] Use 'object' for resolving only specific attributes of 'module' (PY-7823) --- .../python/psi/impl/references/PyReferenceImpl.java | 8 +++++--- .../src/com/jetbrains/python/psi/types/PyModuleType.java | 5 +++-- .../unresolvedTopLevelInit.py | 1 + .../inspections/PyUnresolvedReferencesInspectionTest.java | 5 +++++ 4 files changed, 14 insertions(+), 5 deletions(-) create mode 100644 python/testData/inspections/PyUnresolvedReferencesInspection/unresolvedTopLevelInit.py diff --git a/python/src/com/jetbrains/python/psi/impl/references/PyReferenceImpl.java b/python/src/com/jetbrains/python/psi/impl/references/PyReferenceImpl.java index 14184a573b4f..35b7773b7fe6 100644 --- a/python/src/com/jetbrains/python/psi/impl/references/PyReferenceImpl.java +++ b/python/src/com/jetbrains/python/psi/impl/references/PyReferenceImpl.java @@ -298,10 +298,12 @@ public class PyReferenceImpl implements PsiReferenceEx, PsiPolyVariantReference PyBuiltinCache builtins_cache = PyBuiltinCache.getInstance(realContext); if (uexpr == null) { // ...as a part of current module - PyType otype = builtins_cache.getObjectType(); // "object" as a closest kin to "module" String name = myElement.getName(); - if (otype != null && name != null) { - ret.addAll(otype.resolveMember(name, null, AccessDirection.READ, myContext)); + if (PyModuleType.MODULE_MEMBERS.contains(name)) { + PyType otype = builtins_cache.getObjectType(); // "object" as a closest kin to "module" + if (otype != null && name != null) { + ret.addAll(otype.resolveMember(name, null, AccessDirection.READ, myContext)); + } } } if (uexpr == null) { diff --git a/python/src/com/jetbrains/python/psi/types/PyModuleType.java b/python/src/com/jetbrains/python/psi/types/PyModuleType.java index 6213aef01a37..6a16835a05c9 100644 --- a/python/src/com/jetbrains/python/psi/types/PyModuleType.java +++ b/python/src/com/jetbrains/python/psi/types/PyModuleType.java @@ -40,7 +40,8 @@ public class PyModuleType implements PyType { // Modules don't descend from obje @NotNull private final PyFile myModule; @Nullable private final PyImportedModule myImportedModule; - protected static ImmutableSet ourPossibleFields = ImmutableSet.of("__name__", "__file__", "__path__", "__doc__", "__dict__", "__package__"); + public static final ImmutableSet MODULE_MEMBERS = ImmutableSet.of( + "__name__", "__file__", "__path__", "__doc__", "__dict__", "__package__"); public PyModuleType(@NotNull PyFile source) { this(source, null); @@ -358,7 +359,7 @@ public class PyModuleType implements PyType { // Modules don't descend from obje @NotNull public static Set getPossibleInstanceMembers() { - return ourPossibleFields; + return MODULE_MEMBERS; } } diff --git a/python/testData/inspections/PyUnresolvedReferencesInspection/unresolvedTopLevelInit.py b/python/testData/inspections/PyUnresolvedReferencesInspection/unresolvedTopLevelInit.py new file mode 100644 index 000000000000..bfaf77fe95ca --- /dev/null +++ b/python/testData/inspections/PyUnresolvedReferencesInspection/unresolvedTopLevelInit.py @@ -0,0 +1 @@ +__init__ diff --git a/python/testSrc/com/jetbrains/python/inspections/PyUnresolvedReferencesInspectionTest.java b/python/testSrc/com/jetbrains/python/inspections/PyUnresolvedReferencesInspectionTest.java index 9e7f644ee9ba..da626814890c 100644 --- a/python/testSrc/com/jetbrains/python/inspections/PyUnresolvedReferencesInspectionTest.java +++ b/python/testSrc/com/jetbrains/python/inspections/PyUnresolvedReferencesInspectionTest.java @@ -262,6 +262,11 @@ public class PyUnresolvedReferencesInspectionTest extends PyTestCase { doTest(); } + // PY-7823 + public void testUnresolvedTopLevelInit() { + doTest(); + } + private void doTest() { myFixture.configureByFile(TEST_DIRECTORY + getTestName(true) + ".py"); myFixture.enableInspections(PyUnresolvedReferencesInspection.class); From 2eab0256599801bcb7c3e9dc7733fce00f5bd24f Mon Sep 17 00:00:00 2001 From: Andrey Vlasovskikh Date: Thu, 18 Apr 2013 21:29:49 +0400 Subject: [PATCH 15/17] Added test for MRO in complicated diamond hierarchy (PY-4183) --- .../classMRO/ComplicatedDiamond.py | 30 +++++++++++++++++++ .../python/codeInsight/PyClassMROTest.java | 5 ++++ 2 files changed, 35 insertions(+) create mode 100644 python/testData/codeInsight/classMRO/ComplicatedDiamond.py diff --git a/python/testData/codeInsight/classMRO/ComplicatedDiamond.py b/python/testData/codeInsight/classMRO/ComplicatedDiamond.py new file mode 100644 index 000000000000..e9466b880a3a --- /dev/null +++ b/python/testData/codeInsight/classMRO/ComplicatedDiamond.py @@ -0,0 +1,30 @@ +class A(object): + pass + + +class B(A): + pass + + +class C(A): + pass + + +class D(A): + pass + + +class E(B): + pass + + +class F(B): + pass + + +class G(C, D): + pass + + +class H(E, F, G): + pass diff --git a/python/testSrc/com/jetbrains/python/codeInsight/PyClassMROTest.java b/python/testSrc/com/jetbrains/python/codeInsight/PyClassMROTest.java index 48904fd4e3bf..c506969839f6 100644 --- a/python/testSrc/com/jetbrains/python/codeInsight/PyClassMROTest.java +++ b/python/testSrc/com/jetbrains/python/codeInsight/PyClassMROTest.java @@ -47,6 +47,11 @@ public class PyClassMROTest extends PyTestCase { assertMRO(getClass("G"), "E", "F", "object"); } + // PY-4183 + public void testComplicatedDiamond() { + assertMRO(getClass("H"), "E", "F", "B", "G", "C", "D", "A", "object"); + } + public void assertMRO(@NotNull PyClass cls, @NotNull String... mro) { final List types = cls.getAncestorTypes(TypeEvalContext.fastStubOnly(null)); final List classNames = new ArrayList(); From da62cc4f5d64794a838e579cfa9dd2965eabbaa6 Mon Sep 17 00:00:00 2001 From: Andrey Vlasovskikh Date: Thu, 18 Apr 2013 22:25:51 +0400 Subject: [PATCH 16/17] Use TypeEvalContext for getting ancestor classes if it's available --- .../PyAttributeOutsideInitInspection.java | 2 +- .../PyClassHasNoInitInspection.java | 2 +- .../PyMissingConstructorInspection.java | 18 +++++++++--------- .../PyUnresolvedReferencesInspection.java | 8 ++++---- .../psi/impl/PyCallExpressionHelper.java | 2 +- .../psi/impl/references/PyReferenceImpl.java | 2 +- .../python/psi/types/PyClassTypeImpl.java | 2 +- 7 files changed, 18 insertions(+), 18 deletions(-) diff --git a/python/src/com/jetbrains/python/inspections/PyAttributeOutsideInitInspection.java b/python/src/com/jetbrains/python/inspections/PyAttributeOutsideInitInspection.java index 02cdf65cc08e..554cdc763400 100644 --- a/python/src/com/jetbrains/python/inspections/PyAttributeOutsideInitInspection.java +++ b/python/src/com/jetbrains/python/inspections/PyAttributeOutsideInitInspection.java @@ -65,7 +65,7 @@ public class PyAttributeOutsideInitInspection extends PyInspection { collectAttributesFromSuper(attributesInInit, initMethod); } else { - for (PyClass superClass : containingClass.getAncestorClasses()) { + for (PyClass superClass : containingClass.getAncestorClasses(myTypeEvalContext)) { final PyFunction superInit = superClass.findMethodByName(PyNames.INIT, false); if (superInit != null) PyClassImpl.collectInstanceAttributes(superInit, attributesInInit); diff --git a/python/src/com/jetbrains/python/inspections/PyClassHasNoInitInspection.java b/python/src/com/jetbrains/python/inspections/PyClassHasNoInitInspection.java index 82df134a19b1..0303763563fd 100644 --- a/python/src/com/jetbrains/python/inspections/PyClassHasNoInitInspection.java +++ b/python/src/com/jetbrains/python/inspections/PyClassHasNoInitInspection.java @@ -44,7 +44,7 @@ public class PyClassHasNoInitInspection extends PyInspection { registerProblem(node.getNameIdentifier(), PyBundle.message("INSP.class.has.no.init"), new AddMethodQuickFix("__init__", new PyClassTypeImpl(node, false), false)); } - for (PyClass ancestor : node.getAncestorClasses()) { + for (PyClass ancestor : node.getAncestorClasses(myTypeEvalContext)) { final PyFunction ancestorInit = ancestor.findInitOrNew(false); if (ancestorInit == null) { registerProblem(node.getNameIdentifier(), PyBundle.message("INSP.parent.$0.has.no.init", ancestor.getName()), diff --git a/python/src/com/jetbrains/python/inspections/PyMissingConstructorInspection.java b/python/src/com/jetbrains/python/inspections/PyMissingConstructorInspection.java index 6ce1ab531f45..5cf9950b9d35 100644 --- a/python/src/com/jetbrains/python/inspections/PyMissingConstructorInspection.java +++ b/python/src/com/jetbrains/python/inspections/PyMissingConstructorInspection.java @@ -61,8 +61,8 @@ public class PyMissingConstructorInspection extends PyInspection { } } - private static boolean superHasConstructor(@NotNull PyClass cls) { - for (PyClass c : cls.getAncestorClasses()) { + private boolean superHasConstructor(@NotNull PyClass cls) { + for (PyClass c : cls.getAncestorClasses(myTypeEvalContext)) { final String name = c.getName(); final String className = cls.getName(); if (!OBJECT.equals(name) && !FAKE_OLD_BASE.equals(name) && className != null && @@ -77,7 +77,7 @@ public class PyMissingConstructorInspection extends PyInspection { if (PyBroadExceptionInspection.equalsException(cls, context)) { return true; } - for (PyClass baseClass : cls.getAncestorClasses()) { + for (PyClass baseClass : cls.getAncestorClasses(myTypeEvalContext)) { if (PyBroadExceptionInspection.equalsException(baseClass, context)) { return true; } @@ -85,7 +85,7 @@ public class PyMissingConstructorInspection extends PyInspection { return false; } - private static boolean hasConstructorCall(PyClass node, PyFunction initMethod) { + private boolean hasConstructorCall(PyClass node, PyFunction initMethod) { PyStatementList statementList = initMethod.getStatementList(); CallVisitor visitor = new CallVisitor(node); if (statementList != null) { @@ -95,7 +95,7 @@ public class PyMissingConstructorInspection extends PyInspection { return false; } - private static class CallVisitor extends PyRecursiveElementVisitor { + private class CallVisitor extends PyRecursiveElementVisitor { private boolean myHasConstructorCall = false; private PyClass myClass; CallVisitor(PyClass node) { @@ -108,7 +108,7 @@ public class PyMissingConstructorInspection extends PyInspection { myHasConstructorCall = true; } - private static boolean isConstructorCall(PyCallExpression expression, PyClass cl) { + private boolean isConstructorCall(PyCallExpression expression, PyClass cl) { PyExpression callee = expression.getCallee(); if (callee instanceof PyQualifiedExpression) { PyExpression qualifier = ((PyQualifiedExpression)callee).getQualifier(); @@ -127,7 +127,7 @@ public class PyMissingConstructorInspection extends PyInspection { if (firstArg.equals(cl.getName()) || firstArg.equals(CANONICAL_SELF+"."+ CLASS) || (qualifiedName != null && qualifiedName.endsWith(firstArg))) return true; - for (PyClass s : cl.getAncestorClasses()) { + for (PyClass s : cl.getAncestorClasses(myTypeEvalContext)) { if (firstArg.equals(s.getName())) return true; } @@ -144,7 +144,7 @@ public class PyMissingConstructorInspection extends PyInspection { return false; } - private static boolean isSuperClassCall(PyClass cl, PyExpression qualifier) { + private boolean isSuperClassCall(PyClass cl, PyExpression qualifier) { PsiElement callingClass = null; if (qualifier instanceof PyCallExpression) { PyExpression innerCallee = ((PyCallExpression)qualifier).getCallee(); @@ -159,7 +159,7 @@ public class PyMissingConstructorInspection extends PyInspection { if (ref != null) callingClass = ref.resolve(); } - for (PyClass s : cl.getAncestorClasses()) { + for (PyClass s : cl.getAncestorClasses(myTypeEvalContext)) { if (s.equals(callingClass)) { return true; } diff --git a/python/src/com/jetbrains/python/inspections/PyUnresolvedReferencesInspection.java b/python/src/com/jetbrains/python/inspections/PyUnresolvedReferencesInspection.java index 37e76f0108bd..ec905ceabdac 100644 --- a/python/src/com/jetbrains/python/inspections/PyUnresolvedReferencesInspection.java +++ b/python/src/com/jetbrains/python/inspections/PyUnresolvedReferencesInspection.java @@ -134,7 +134,7 @@ public class PyUnresolvedReferencesInspection extends PyInspection { final List slots = pyClass.getSlots(); final String attrName = node.getReferencedName(); if (slots != null && !slots.contains(attrName) && !slots.contains(PyNames.DICT)) { - for (PyClass ancestor : pyClass.getAncestorClasses()) { + for (PyClass ancestor : pyClass.getAncestorClasses(myTypeEvalContext)) { if (ancestor == null) { return; } @@ -628,7 +628,7 @@ public class PyUnresolvedReferencesInspection extends PyInspection { return null; } - private static boolean ignoreUnresolvedMemberForType(@NotNull PyType qtype, PsiReference reference, String refText) { + private boolean ignoreUnresolvedMemberForType(@NotNull PyType qtype, PsiReference reference, String refText) { if (qtype instanceof PyNoneType || PyTypeChecker.isUnknown(qtype)) { // this almost always means that we don't know the type, so don't show an error in this case return true; @@ -667,12 +667,12 @@ public class PyUnresolvedReferencesInspection extends PyInspection { return false; } - private static boolean isDecoratedAsDynamic(@NotNull PyClass cls, boolean inherited) { + private boolean isDecoratedAsDynamic(@NotNull PyClass cls, boolean inherited) { if (inherited) { if (isDecoratedAsDynamic(cls, false)) { return true; } - for (PyClass base : cls.getAncestorClasses()) { + for (PyClass base : cls.getAncestorClasses(myTypeEvalContext)) { if (base != null && isDecoratedAsDynamic(base, false)) { return true; } diff --git a/python/src/com/jetbrains/python/psi/impl/PyCallExpressionHelper.java b/python/src/com/jetbrains/python/psi/impl/PyCallExpressionHelper.java index 9c78ef8a688c..a996987d1938 100644 --- a/python/src/com/jetbrains/python/psi/impl/PyCallExpressionHelper.java +++ b/python/src/com/jetbrains/python/psi/impl/PyCallExpressionHelper.java @@ -505,7 +505,7 @@ public class PyCallExpressionHelper { return getSuperClassUnionType(firstClass); } if (secondClass.isSubclass(firstClass)) { - final Iterator iterator = firstClass.getAncestorClasses().iterator(); + final Iterator iterator = firstClass.getAncestorClasses(context).iterator(); if (iterator.hasNext()) { return new PyClassTypeImpl(iterator.next(), false); // super(Foo, self) has type of Foo, modulo __get__() } diff --git a/python/src/com/jetbrains/python/psi/impl/references/PyReferenceImpl.java b/python/src/com/jetbrains/python/psi/impl/references/PyReferenceImpl.java index 35b7773b7fe6..b50b16e490ac 100644 --- a/python/src/com/jetbrains/python/psi/impl/references/PyReferenceImpl.java +++ b/python/src/com/jetbrains/python/psi/impl/references/PyReferenceImpl.java @@ -125,7 +125,7 @@ public class PyReferenceImpl implements PsiReferenceEx, PsiPolyVariantReference it.set(rrr.replace(init)); } else { // init not found; maybe it's ancestor's - for (PyClass ancestor : cls.getAncestorClasses()) { + for (PyClass ancestor : cls.getAncestorClasses(myContext.getTypeEvalContext())) { init = ancestor.findMethodByName(PyNames.INIT, false); if (init != null) { // add to results as low priority diff --git a/python/src/com/jetbrains/python/psi/types/PyClassTypeImpl.java b/python/src/com/jetbrains/python/psi/types/PyClassTypeImpl.java index 0b24f8100746..b172ec83553b 100644 --- a/python/src/com/jetbrains/python/psi/types/PyClassTypeImpl.java +++ b/python/src/com/jetbrains/python/psi/types/PyClassTypeImpl.java @@ -155,7 +155,7 @@ public class PyClassTypeImpl extends UserDataHolderBase implements PyClassType { PyType first_arg_type = context.getType(first_arg); if (first_arg_type instanceof PyClassType) { PyClass derived_class = ((PyClassType)first_arg_type).getPyClass(); - final Iterator base_it = derived_class.getAncestorClasses().iterator(); + final Iterator base_it = derived_class.getAncestorClasses(context).iterator(); if (base_it.hasNext()) { return new PyClassTypeImpl(base_it.next(), true).resolveMember(name, location, direction, resolveContext); } From 8285e19d323df6cb01555c5d10f5d1614ebc71e9 Mon Sep 17 00:00:00 2001 From: Andrey Vlasovskikh Date: Fri, 19 Apr 2013 18:53:01 +0400 Subject: [PATCH 17/17] Added class ancestors cache --- .../python/psi/impl/PyClassImpl.java | 32 ++++++++++++++++++- 1 file changed, 31 insertions(+), 1 deletion(-) diff --git a/python/src/com/jetbrains/python/psi/impl/PyClassImpl.java b/python/src/com/jetbrains/python/psi/impl/PyClassImpl.java index f5079648c79f..f3f047fd53b4 100644 --- a/python/src/com/jetbrains/python/psi/impl/PyClassImpl.java +++ b/python/src/com/jetbrains/python/psi/impl/PyClassImpl.java @@ -52,6 +52,24 @@ public class PyClassImpl extends PyPresentableElementImpl implement private volatile Map myPropertyCache; + private class CachedAncestorsProvider implements CachedValueProvider> { + @Nullable private TypeEvalContext myCachedContext; + + @Nullable + @Override + public Result> compute() { + final TypeEvalContext context = myCachedContext != null ? myCachedContext : TypeEvalContext.fastStubOnly(null); + final List ancestorTypes = isNewStyleClass() ? getMROAncestorTypes(context) : getOldStyleAncestorTypes(context); + return Result.create(ancestorTypes, PsiModificationTracker.OUT_OF_CODE_BLOCK_MODIFICATION_COUNT); + } + + private void setTypeEvalContext(@Nullable TypeEvalContext cachedContext) { + myCachedContext = cachedContext; + } + } + + private final CachedAncestorsProvider myCachedAncestorsProvider = new CachedAncestorsProvider(); + @Override public PyType getType(@NotNull TypeEvalContext context, @NotNull TypeEvalContext.Key key) { return new PyClassTypeImpl(this, true); @@ -1063,7 +1081,19 @@ public class PyClassImpl extends PyPresentableElementImpl implement @NotNull @Override public List getAncestorTypes(@NotNull TypeEvalContext context) { - return isNewStyleClass() ? getMROAncestorTypes(context) : getOldStyleAncestorTypes(context); + return calculateAncestorTypes(context); + } + + @NotNull + private List calculateAncestorTypes(@NotNull TypeEvalContext context) { + myCachedAncestorsProvider.setTypeEvalContext(context); + try { + // TODO: Return different cached copies depending on the type eval context parameters + return CachedValuesManager.getManager(getProject()).getCachedValue(this, myCachedAncestorsProvider); + } + finally { + myCachedAncestorsProvider.setTypeEvalContext(null); + } } @NotNull