PyClass.iterateAncestors() split into iterateAncestors() (returning class refs, which can be not classes or unresolved classes) and iterateAncestorClasses() (returning actual resolved classes); fix PY-817

This commit is contained in:
Dmitry Jemerov
2011-01-25 14:15:31 +01:00
parent eafb6cfac3
commit 0e38f47f21
19 changed files with 217 additions and 97 deletions
@@ -426,7 +426,7 @@ public class PythonDocumentationProvider extends QuickDocumentationProvider {
if (cls != null && meth_name != null) {
final boolean is_constructor = PyNames.INIT.equals(meth_name);
// look for inherited and its doc
Iterable<PyClass> classes = cls.iterateAncestors();
Iterable<PyClass> classes = cls.iterateAncestorClasses();
if (is_constructor) {
// look at our own class again and maybe inherit class's doc
classes = new ChainIterable<PyClass>(cls).add(classes);
@@ -496,9 +496,9 @@ public class PythonDocumentationProvider extends QuickDocumentationProvider {
PyClass cls = inferContainingClassOf(context);
if (cls != null) {
String desired_name = link.substring(LINK_TYPE_PARENT.length());
for (PyClass parent : cls.iterateAncestors()) {
final String parent_name = parent.getName();
if (parent_name != null && parent_name.equals(desired_name)) return parent;
for (PyClassRef parent : cls.iterateAncestors()) {
final String parent_name = parent.getClassName();
if (parent_name != null && parent_name.equals(desired_name)) return parent.getPyClass();
}
}
}
@@ -96,7 +96,7 @@ public class AddFieldQuickFix implements LocalQuickFix {
return appendToInit(init, callback);
}
else { // no init! boldly copy ancestor's.
for (PyClass ancestor : cls.iterateAncestors()) {
for (PyClass ancestor : cls.iterateAncestorClasses()) {
init = ancestor.findMethodByName(PyNames.INIT, false);
if (init != null) break;
}
@@ -45,13 +45,13 @@ public class PyGotoSuperHandler implements CodeInsightActionHandler {
else {
PyClass pyClass = PsiTreeUtil.getParentOfType(element, PyClass.class);
if (pyClass != null) {
PyClass[] superClasses = PyUtil.getAllSuperClasses(pyClass);
if (superClasses.length != 0) {
if (superClasses.length == 1) {
superClasses[0].navigate(true);
List<PyClass> superClasses = PyUtil.getAllSuperClasses(pyClass);
if (superClasses.size() != 0) {
if (superClasses.size() == 1) {
superClasses.get(0).navigate(true);
}
else {
NavigationUtil.getPsiElementPopup(superClasses, CodeInsightBundle.message("goto.super.class.chooser.title"))
NavigationUtil.getPsiElementPopup(superClasses.toArray(new PyClass[superClasses.size()]), CodeInsightBundle.message("goto.super.class.chooser.title"))
.showInBestPositionFor(editor);
}
}
@@ -66,7 +66,7 @@ public class PyGotoSuperHandler implements CodeInsightActionHandler {
return Collections.emptyList();
}
final List<PyFunction> result = new ArrayList<PyFunction>();
for (PyClass aClass: pyClass.iterateAncestors()) {
for (PyClass aClass: pyClass.iterateAncestorClasses()) {
final PyFunction byName = aClass.findMethodByName(name, false);
if (byName != null) {
result.add(byName);
@@ -8,7 +8,10 @@ import com.intellij.psi.PsiElement;
import com.intellij.psi.PsiWhiteSpace;
import com.intellij.psi.util.PsiTreeUtil;
import com.intellij.util.ProcessingContext;
import com.jetbrains.python.psi.*;
import com.jetbrains.python.psi.PyClass;
import com.jetbrains.python.psi.PyFunction;
import com.jetbrains.python.psi.PyReferenceExpression;
import com.jetbrains.python.psi.PyTargetExpression;
import org.jetbrains.annotations.NotNull;
import java.util.List;
@@ -42,7 +45,7 @@ public class PySuperAttributesCompletionContributor extends CompletionContributo
for (PyTargetExpression expr : containingClass.getClassAttributes()) {
seenNames.add(expr.getName());
}
for (PyClass ancestor : containingClass.iterateAncestors()) {
for (PyClass ancestor : containingClass.iterateAncestorClasses()) {
for (PyTargetExpression expr : ancestor.getClassAttributes()) {
if (!seenNames.contains(expr.getName())) {
result.addElement(LookupElementBuilder.create(expr, expr.getName() + " = "));
@@ -41,7 +41,7 @@ public class PySuperMethodCompletionContributor extends CompletionContributor {
for (PyFunction function : containingClass.getMethods()) {
seenNames.add(function.getName());
}
for (PyClass ancestor : containingClass.iterateAncestors()) {
for (PyClass ancestor : containingClass.iterateAncestorClasses()) {
for (PyFunction superMethod : ancestor.getMethods()) {
if (!seenNames.contains(superMethod.getName())) {
result.addElement(LookupElementBuilder.create(superMethod.getName() + superMethod.getParameterList().getText() + ":"));
@@ -219,7 +219,7 @@ public class PyOverrideImplementUtil {
@NotNull
private static Collection<PyFunction> getAllSuperFunctions(@NotNull final PyClass pyClass) {
final Map<String, PyFunction> superFunctions = new HashMap<String, PyFunction>();
for (PyClass aClass : pyClass.iterateAncestors()) {
for (PyClass aClass : pyClass.iterateAncestorClasses()) {
for (PyFunction function : aClass.getMethods()) {
if (!superFunctions.containsKey(function.getName())) {
superFunctions.put(function.getName(), function);
@@ -7,15 +7,17 @@ import com.jetbrains.python.psi.PyClass;
import com.jetbrains.python.psi.PyUtil;
import org.jetbrains.annotations.NotNull;
import java.util.List;
/**
* @author Alexey.Ivanov
*/
public class PyTypeHierarchyTreeStructure extends PySubTypesHierarchyTreeStructure {
private static PyTypeHierarchyNodeDescriptor buildHierarchyElement(@NotNull final PyClass cl) {
PyTypeHierarchyNodeDescriptor descriptor = null;
PyClass[] superClasses = PyUtil.getAllSuperClasses(cl);
for (int i = superClasses.length - 1; i >= 0; --i) {
final PyClass superClass = superClasses[i];
List<PyClass> superClasses = PyUtil.getAllSuperClasses(cl);
for (int i = superClasses.size() - 1; i >= 0; --i) {
final PyClass superClass = superClasses.get(i);
final PyTypeHierarchyNodeDescriptor newDescriptor = new PyTypeHierarchyNodeDescriptor(descriptor, superClass, false);
if (descriptor != null) {
descriptor.setCachedChildren(new PyTypeHierarchyNodeDescriptor[]{newDescriptor});
@@ -44,8 +44,8 @@ public class PyExceptionInheritInspection extends PyInspection {
PsiElement psiElement = ((PyReferenceExpression)callee).getReference().resolve();
if (psiElement instanceof PyClass) {
PyClass aClass = (PyClass) psiElement;
for (PyClass pyClass : aClass.iterateAncestors()) {
if ("Exception".equals(pyClass.getName())) {
for (PyClassRef pyClass : aClass.iterateAncestors()) {
if ("Exception".equals(pyClass.getClassName())) {
return;
}
}
@@ -104,7 +104,9 @@ public interface PyClass extends
* A lazy way to list ancestor classes width first, in method-resolution order (MRO).
* @return an iterable of ancestor classes.
*/
Iterable<PyClass> iterateAncestors();
Iterable<PyClassRef> iterateAncestors();
Iterable<PyClass> iterateAncestorClasses();
/**
* Scan properties in order of definition, until processor returns true for one of them.
@@ -0,0 +1,48 @@
package com.jetbrains.python.psi;
import com.intellij.psi.PsiElement;
import org.jetbrains.annotations.Nullable;
/**
* @author yole
*/
public class PyClassRef {
@Nullable
private final PsiElement myElement;
public PyClassRef(PsiElement element) {
myElement = element;
}
@Nullable
public PyClass getPyClass() {
return myElement instanceof PyClass ? (PyClass) myElement : null;
}
@Nullable
public String getClassName() {
return myElement instanceof PyClass ? ((PyClass) myElement).getName() : null;
}
@Nullable
public String getQualifiedName() {
return myElement instanceof PyClass ? ((PyClass) myElement).getQualifiedName() : 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;
}
}
@@ -342,15 +342,9 @@ public class PyUtil {
* Collects superclasses of a class all the way up the inheritance chain. The order is <i>not</i> necessarily the MRO.
*/
@NotNull
public static PyClass[] getAllSuperClasses(@NotNull PyClass pyClass) {
Set<PyClass> superClasses = getAllSuperClassesSet(pyClass);
return superClasses.toArray(new PyClass[superClasses.size()]);
}
@NotNull
public static Set<PyClass> getAllSuperClassesSet(PyClass pyClass) {
Set<PyClass> superClasses = new HashSet<PyClass>();
for (PyClass ancestor : pyClass.iterateAncestors()) superClasses.add(ancestor);
public static List<PyClass> getAllSuperClasses(@NotNull PyClass pyClass) {
List<PyClass> superClasses = new ArrayList<PyClass>();
for (PyClass ancestor : pyClass.iterateAncestorClasses()) superClasses.add(ancestor);
return superClasses;
}
@@ -721,7 +715,7 @@ public class PyUtil {
}
public static boolean isExceptionClass(PyClass pyClass) {
for (PyClass c: pyClass.iterateAncestors()) {
for (PyClassRef c: pyClass.iterateAncestors()) {
if ("BaseException".equals(c.getQualifiedName())) {
return true;
}
@@ -776,7 +770,7 @@ public class PyUtil {
Set<PyFunction.Flag> flags = detectDecorationsAndWrappersOf(node);
boolean isMetaclassMethod = false;
PyClass type_cls = PyBuiltinCache.getInstance(node).getClass("type");
for (PyClass ancestor_cls : cls.iterateAncestors()) {
for (PyClass ancestor_cls : cls.iterateAncestorClasses()) {
if (ancestor_cls == type_cls) {
isMetaclassMethod = true;
break;
@@ -143,13 +143,18 @@ public class PyClassImpl extends PyPresentableElementImpl<PyClassStub> implement
/* 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.
*/
public Iterable<PyClass> iterateAncestors() {
public Iterable<PyClassRef> iterateAncestors() {
return new AncestorsIterable(this);
}
@Override
public Iterable<PyClass> iterateAncestorClasses() {
return new AncestorClassesIterable(this);
}
public boolean isSubclass(PyClass parent) {
if (this == parent) return true;
for (PyClass superclass : iterateAncestors()) {
for (PyClass superclass : iterateAncestorClasses()) {
if (parent == superclass) return true;
}
return false;
@@ -193,7 +198,7 @@ public class PyClassImpl extends PyPresentableElementImpl<PyClassStub> implement
if (slots != null) {
return slots;
}
for(PyClass cls: iterateAncestors()) {
for(PyClass cls: iterateAncestorClasses()) {
slots = ((PyClassImpl) cls).getOwnSlots();
if (slots != null) {
return slots;
@@ -211,31 +216,34 @@ public class PyClassImpl extends PyPresentableElementImpl<PyClassStub> implement
return PyFileImpl.getStringListFromTargetExpression(PyNames.SLOTS, getClassAttributes());
}
protected List<PyClass> getSuperClassesList() {
protected List<PyClassRef> getSuperClassesList() {
if (PyNames.FAKE_OLD_BASE.equals(getName())) {
return Collections.emptyList();
}
List<PyClassRef> result = new ArrayList<PyClassRef>();
List<PyClass> superClasses = resolveSuperClassesFromStub();
if (superClasses == null) {
superClasses = new ArrayList<PyClass>();
if (superClasses != null) {
for (PyClass superClass : superClasses) {
result.add(new PyClassRef(superClass));
}
}
else {
PsiElement[] superClassElements = getSuperClassElements();
for (PsiElement element : superClassElements) {
if (element instanceof PyClass) {
superClasses.add((PyClass)element);
}
result.add(new PyClassRef(element));
}
}
if (superClasses.size() == 0 && !PyBuiltinCache.getInstance(this).hasInBuiltins(this)) {
if (result.size() == 0 && !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) {
superClasses.add(implicitSuperclass);
result.add(new PyClassRef(implicitSuperclass));
}
}
return superClasses;
return result;
}
@Nullable
@@ -508,7 +516,7 @@ public class PyClassImpl extends PyPresentableElementImpl<PyClassStub> implement
};
Property prop = findPropertyLocally(name_filer, null, use_advanced_syntax);
if (prop != null) return prop;
for (PyClass cls : iterateAncestors()) {
for (PyClass cls : iterateAncestorClasses()) {
prop = ((PyClassImpl)cls).findPropertyLocally(name_filer, null, use_advanced_syntax);
if (prop != null) return prop;
}
@@ -529,7 +537,7 @@ public class PyClassImpl extends PyPresentableElementImpl<PyClassStub> implement
Property property = findPropertyLocally(null, processor, use_advanced_syntax);
if (property != null) return property;
if (inherited) {
for (PyClass cls : iterateAncestors()) {
for (PyClass cls : iterateAncestorClasses()) {
property = ((PyClassImpl)cls).findPropertyLocally(null, processor, use_advanced_syntax);
if (property != null) return property;
}
@@ -624,7 +632,7 @@ public class PyClassImpl extends PyPresentableElementImpl<PyClassStub> implement
if (! processor.process(method)) return false;
}
if (inherited) {
for (PyClass ancestor : iterateAncestors()) {
for (PyClass ancestor : iterateAncestorClasses()) {
if (!ancestor.visitMethods(processor, false)) {
return false;
}
@@ -639,7 +647,7 @@ public class PyClassImpl extends PyPresentableElementImpl<PyClassStub> implement
if (!processor.process(nestedClass)) return false;
}
if (inherited) {
for (PyClass ancestor : iterateAncestors()) {
for (PyClass ancestor : iterateAncestorClasses()) {
if (!((PyClassImpl) ancestor).visitNestedClasses(processor, false)) {
return false;
}
@@ -654,7 +662,7 @@ public class PyClassImpl extends PyPresentableElementImpl<PyClassStub> implement
if (! processor.process(attribute)) return false;
}
if (inherited) {
for (PyClass ancestor : iterateAncestors()) {
for (PyClass ancestor : iterateAncestorClasses()) {
if (!ancestor.visitClassAttributes(processor, false)) {
return false;
}
@@ -767,9 +775,14 @@ public class PyClassImpl extends PyPresentableElementImpl<PyClassStub> implement
PyClass objclass = PyBuiltinCache.getInstance(this).getClass("object");
if (this == objclass) return true; // a rare but possible case
if (hasNewStyleMetaClass(this)) return true;
for (PyClass ancestor : iterateAncestors()) {
if (ancestor == objclass) return true;
if (hasNewStyleMetaClass(ancestor)) {
for (PyClassRef ancestor : iterateAncestors()) {
PyClass pyClass = ancestor.getPyClass();
if (pyClass == null) {
// unknown, assume new-style class
return true;
}
if (pyClass == objclass) return true;
if (hasNewStyleMetaClass(pyClass)) {
return true;
}
}
@@ -906,32 +919,32 @@ public class PyClassImpl extends PyPresentableElementImpl<PyClassStub> implement
return reference != null ? reference.get() : null;
}
private static class AncestorsIterable implements Iterable<PyClass> {
private static class AncestorsIterable implements Iterable<PyClassRef> {
private final PyClassImpl myClass;
public AncestorsIterable(final PyClassImpl pyClass) {
myClass = pyClass;
}
public Iterator<PyClass> iterator() {
public Iterator<PyClassRef> iterator() {
return new AncestorsIterator(myClass);
}
}
private static class AncestorsIterator implements Iterator<PyClass> {
List<PyClassImpl> pending = new LinkedList<PyClassImpl>();
Set<PyClass> seen;
Iterator<PyClass> percolator;
PyClass prefetch = null;
private static class AncestorsIterator implements Iterator<PyClassRef> {
List<PyClassRef> pending = new LinkedList<PyClassRef>();
private final Set<PyClassRef> seen;
Iterator<PyClassRef> percolator;
PyClassRef prefetch = null;
private final PyClassImpl myAClass;
public AncestorsIterator(PyClassImpl aClass) {
myAClass = aClass;
percolator = myAClass.getSuperClassesList().iterator();
seen = new HashSet<PyClass>();
seen = new HashSet<PyClassRef>();
}
private AncestorsIterator(PyClassImpl AClass, Set<PyClass> seen) {
private AncestorsIterator(PyClassImpl AClass, Set<PyClassRef> seen) {
myAClass = AClass;
this.seen = seen;
percolator = myAClass.getSuperClassesList().iterator();
@@ -944,23 +957,23 @@ public class PyClassImpl extends PyPresentableElementImpl<PyClassStub> implement
return prefetch != null;
}
public PyClass next() {
final PyClass nextClass = getNext();
public PyClassRef next() {
final PyClassRef nextClass = getNext();
if (nextClass == null) throw new NoSuchElementException();
return nextClass;
}
@Nullable
private PyClass getNext() {
private PyClassRef getNext() {
iterations:
while (true) {
if (prefetch != null) {
PyClass ret = prefetch;
PyClassRef ret = prefetch;
prefetch = null;
return ret;
}
if (percolator.hasNext()) {
PyClassImpl it = (PyClassImpl)percolator.next();
PyClassRef it = percolator.next();
if (seen.contains(it)) {
continue iterations; // loop back is equivalent to return next();
}
@@ -968,13 +981,18 @@ public class PyClassImpl extends PyPresentableElementImpl<PyClassStub> implement
seen.add(it);
return it;
}
else if (pending.size() > 0) {
PyClassImpl it = pending.get(0);
pending.remove(0); // t, ts* = pending
percolator = new AncestorsIterator(it, new HashSet<PyClass>(seen));
// loop back is equivalent to return next();
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<PyClassRef>(seen));
continue iterations;
}
}
return null;
}
else return null;
}
}
@@ -983,5 +1001,59 @@ public class PyClassImpl extends PyPresentableElementImpl<PyClassStub> implement
}
}
private static class AncestorClassesIterable implements Iterable<PyClass> {
private final PyClassImpl myClass;
public AncestorClassesIterable(final PyClassImpl pyClass) {
myClass = pyClass;
}
public Iterator<PyClass> iterator() {
return new AncestorClassesIterator(new AncestorsIterator(myClass));
}
}
private static class AncestorClassesIterator implements Iterator<PyClass> {
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;
}
@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();
}
}
}
@@ -114,7 +114,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.iterateAncestors()) {
for (PyClass ancestor : cls.iterateAncestorClasses()) {
init = ancestor.findMethodByName(PyNames.INIT, false);
if (init != null) {
// add to resuls as low priority
@@ -20,7 +20,7 @@ public class PySuperMethodsSearchExecutor implements QueryExecutor<PsiElement, P
PyClass containingClass = func.getContainingClass();
Set<PyClass> foundMethodContainingClasses = new HashSet<PyClass>();
if (name != null && containingClass != null) {
for (PyClass superClass : containingClass.iterateAncestors()) {
for (PyClass superClass : containingClass.iterateAncestorClasses()) {
if (!queryParameters.isDeepSearch()) {
boolean isAlreadyFound = false;
for (PyClass alreadyFound : foundMethodContainingClasses) {
@@ -128,7 +128,7 @@ public class PyClassType extends UserDataHolderBase implements PyType {
return new SmartList<PsiElement>(classMember);
}
for (PyClass superClass : myClass.iterateAncestors()) {
for (PyClass superClass : myClass.iterateAncestorClasses()) {
PsiElement superMember = resolveClassMember(superClass, name, null);
if (superMember != null) {
return new SmartList<PsiElement>(superMember);
@@ -103,7 +103,7 @@ public class PyStructureViewElement implements StructureViewTreeElement {
if (element instanceof PyClass) {
PyClass the_exception = PyBuiltinCache.getInstance(element).getClass("Exception");
final PyClass cls = (PyClass)element;
for (PyClass anc : cls.iterateAncestors()) {
for (PyClass anc : cls.iterateAncestorClasses()) {
if (anc == the_exception) {
((PyStructureViewElement)(children[i])).setIcon(Icons.EXCEPTION_CLASS_ICON);
break;
@@ -8,7 +8,6 @@ import org.jetbrains.annotations.NotNull;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
import java.util.regex.Pattern;
@@ -80,10 +79,9 @@ public class PythonUnitTestUtil {
}
public static boolean isTestCaseClass(@NotNull PyClass cls, Set<String> testQualifiedNames) {
for (PyClass ancestor : cls.iterateAncestors()) {
if (ancestor == null) continue;
for (PyClassRef ancestor : cls.iterateAncestors()) {
String qName = ancestor.getQualifiedName();
if (qName == null) continue;
if (testQualifiedNames.contains(qName)) {
return true;
}
@@ -2,10 +2,7 @@ package com.jetbrains.python.testing.pytest;
import com.google.common.collect.Lists;
import com.google.common.collect.Sets;
import com.jetbrains.python.psi.PyClass;
import com.jetbrains.python.psi.PyFile;
import com.jetbrains.python.psi.PyFunction;
import com.jetbrains.python.psi.PyStatement;
import com.jetbrains.python.psi.*;
import java.util.HashSet;
import java.util.List;
@@ -40,26 +37,21 @@ public class PyTestUtil {
}
public static boolean isPyTestClass(PyClass pyClass) {
for (PyClass ancestor : pyClass.iterateAncestors()) {
if (ancestor == null) continue;
for (PyClassRef ancestor : pyClass.iterateAncestors()) {
String qName = ancestor.getQualifiedName();
if (PYTHON_TEST_QUALIFIED_CLASSES.contains(qName)) {
return true;
}
String name = pyClass.getName().toLowerCase();
if (name != null && name.startsWith("test")) {
List<PyStatement> result = Lists.newArrayList();
for (PyFunction cls : pyClass.getMethods()) {
if (isPyTestFunction(cls)) {
result.add(cls);
}
}
String name = pyClass.getName().toLowerCase();
if (name != null && name.startsWith("test")) {
for (PyFunction cls : pyClass.getMethods()) {
if (isPyTestFunction(cls)) {
return true;
}
if (!result.isEmpty()) return true;
}
}
return false;
}
}
@@ -13,3 +13,12 @@ class D(C):
class E:
__metaclass__ = None
def create_meta():
return type
Meta = create_meta()
class Something(Meta):
pass