Merge branch 'new-mro'

Conflicts:
	python/src/META-INF/python-plugin-common.xml
This commit is contained in:
Andrey Vlasovskikh
2013-04-19 19:49:43 +04:00
52 changed files with 674 additions and 529 deletions
@@ -11,18 +11,17 @@ 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;
import java.util.ArrayList;
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 +85,35 @@ 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();
}
@NotNull
@Override
public List<PyClassLikeType> getSuperClassTypes(@NotNull TypeEvalContext context) {
final List<PyClassLikeType> result = new ArrayList<PyClassLikeType>();
for (PsiClass cls : myClass.getSupers()) {
result.add(new PyJavaClassType(cls, myDefinition));
}
return result;
}
@Override
public boolean isValid() {
return myClass.isValid();
}
}
@@ -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.PyClassLikeType;
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<PyClassLikeType> getSuperClassTypes(@NotNull TypeEvalContext context);
@NotNull
PyClass[] getSuperClasses();
@@ -105,24 +110,17 @@ 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<PyClassRef> iterateAncestors();
@NotNull
List<PyClass> getAncestorClasses();
Iterable<PyClass> iterateAncestorClasses();
@NotNull
List<PyClass> getAncestorClasses(@NotNull TypeEvalContext context);
/**
* Return the method resolution order list for this class.
* <br/>
* see http://www.python.org/download/releases/2.3/mro/
* <br/>
* <i>Note: the list begins with this class.</i> 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.
* Get a list of all ancestor types.
*/
@NotNull List<PyClass> getMRO();
@NotNull
List<PyClassLikeType> getAncestorTypes(@NotNull TypeEvalContext context);
/**
* Scan properties in order of definition, until processor returns true for one of them.
@@ -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;
}
}
@@ -0,0 +1,23 @@
package com.jetbrains.python.psi.types;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import java.util.List;
/**
* @author vlan
*/
public interface PyClassLikeType extends PyCallableType {
boolean isDefinition();
PyClassLikeType toInstance();
@Nullable
String getClassQName();
@NotNull
List<PyClassLikeType> getSuperClassTypes(@NotNull TypeEvalContext context);
boolean isValid();
}
@@ -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();
}
@@ -893,7 +893,6 @@
<!-- SQLAlchemy -->
<typeProvider implementation="com.jetbrains.sqlalchemy.SQLAlchemyTypeProvider"/>
<pyClassMembersProvider implementation="com.jetbrains.sqlalchemy.SQLAlchemyClassMembersProvider"/>
<knownDecoratorProvider implementation="com.jetbrains.sqlalchemy.SQLAlchemyKnownDecoratorProvider"/>
</extensions>
@@ -70,7 +70,7 @@ public class PyGotoSuperHandler implements CodeInsightActionHandler {
return Collections.emptyList();
}
final List<PyTargetExpression> result = new ArrayList<PyTargetExpression>();
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<PyFunction> result = new ArrayList<PyFunction>();
for (PyClass aClass: pyClass.iterateAncestorClasses()) {
for (PyClass aClass: pyClass.getAncestorClasses()) {
final PyFunction byName = aClass.findMethodByName(name, false);
if (byName != null) {
result.add(byName);
@@ -108,7 +108,7 @@ public class PyLineMarkerProvider implements LineMarkerProvider, PyLineSeparator
List<PsiElement> result = new ArrayList<PsiElement>();
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<PsiElement>(element, element.getTextRange().getStartOffset(),
@@ -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());
@@ -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();
@@ -239,7 +239,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.iterateAncestorClasses()) {
for (PyClass aClass : pyClass.getAncestorClasses()) {
for (PyFunction function : aClass.getMethods()) {
if (!superFunctions.containsKey(function.getName())) {
superFunctions.put(function.getName(), function);
@@ -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<PyClass> classes = cls.iterateAncestorClasses();
Iterable<PyClass> classes = cls.getAncestorClasses();
if (is_constructor) {
// look at our own class again and maybe inherit class's doc
classes = new ChainIterable<PyClass>(cls).add(classes);
@@ -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.getAncestorClasses()) {
final String parent_name = parent.getName();
if (parent_name != null && parent_name.equals(desired_name)) return parent;
}
}
}
@@ -65,7 +65,7 @@ public class PyAttributeOutsideInitInspection extends PyInspection {
collectAttributesFromSuper(attributesInInit, initMethod);
}
else {
for (PyClass superClass : containingClass.iterateAncestorClasses()) {
for (PyClass superClass : containingClass.getAncestorClasses(myTypeEvalContext)) {
final PyFunction superInit = superClass.findMethodByName(PyNames.INIT, false);
if (superInit != null)
PyClassImpl.collectInstanceAttributes(superInit, attributesInInit);
@@ -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(myTypeEvalContext)) {
final PyFunction ancestorInit = ancestor.findInitOrNew(false);
if (ancestorInit == null) {
registerProblem(node.getNameIdentifier(), PyBundle.message("INSP.parent.$0.has.no.init", ancestor.getName()),
@@ -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,9 +48,12 @@ 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();
if ("BaseException".equals(name) || "Exception".equals(name)) {
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;
}
}
@@ -61,8 +61,8 @@ public class PyMissingConstructorInspection extends PyInspection {
}
}
private static boolean superHasConstructor(@NotNull PyClass cls) {
for (PyClass c : cls.iterateAncestorClasses()) {
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.iterateAncestorClasses()) {
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.iterateAncestorClasses()) {
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.iterateAncestorClasses()) {
for (PyClass s : cl.getAncestorClasses(myTypeEvalContext)) {
if (s.equals(callingClass)) {
return true;
}
@@ -134,8 +134,7 @@ public class PyUnresolvedReferencesInspection extends PyInspection {
final List<String> 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.getAncestorClasses(myTypeEvalContext)) {
if (ancestor == null) {
return;
}
@@ -629,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;
@@ -668,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.iterateAncestorClasses()) {
for (PyClass base : cls.getAncestorClasses(myTypeEvalContext)) {
if (base != null && isDecoratedAsDynamic(base, false)) {
return true;
}
@@ -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;
}
@@ -375,7 +375,7 @@ public class PyUtil {
@NotNull
public static List<PyClass> getAllSuperClasses(@NotNull PyClass pyClass) {
List<PyClass> superClasses = new ArrayList<PyClass>();
for (PyClass ancestor : pyClass.iterateAncestorClasses()) {
for (PyClass ancestor : pyClass.getAncestorClasses()) {
if (!PyNames.FAKE_OLD_BASE.equals(ancestor.getName())) {
superClasses.add(ancestor);
}
@@ -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;
}
@@ -1016,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;
@@ -1048,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;
}
@@ -505,7 +505,7 @@ public class PyCallExpressionHelper {
return getSuperClassUnionType(firstClass);
}
if (secondClass.isSubclass(firstClass)) {
final Iterator<PyClass> iterator = firstClass.iterateAncestorClasses().iterator();
final Iterator<PyClass> iterator = firstClass.getAncestorClasses(context).iterator();
if (iterator.hasNext()) {
return new PyClassTypeImpl(iterator.next(), false); // super(Foo, self) has type of Foo, modulo __get__()
}
@@ -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.*;
@@ -26,10 +27,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;
@@ -54,6 +52,24 @@ public class PyClassImpl extends PyPresentableElementImpl<PyClassStub> implement
private volatile Map<String, Property> myPropertyCache;
private class CachedAncestorsProvider implements CachedValueProvider<List<PyClassLikeType>> {
@Nullable private TypeEvalContext myCachedContext;
@Nullable
@Override
public Result<List<PyClassLikeType>> compute() {
final TypeEvalContext context = myCachedContext != null ? myCachedContext : TypeEvalContext.fastStubOnly(null);
final List<PyClassLikeType> 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);
@@ -175,22 +191,29 @@ public class PyClassImpl extends PyPresentableElementImpl<PyClassStub> implement
return expression;
}
public Iterable<PyClassRef> 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);
@NotNull
@Override
public List<PyClass> getAncestorClasses() {
return getAncestorClasses(TypeEvalContext.fastStubOnly(null));
}
@NotNull
@Override
public Iterable<PyClass> iterateAncestorClasses() {
return new AncestorClassesIterable(this);
public List<PyClass> getAncestorClasses(@NotNull TypeEvalContext context) {
final List<PyClass> results = new ArrayList<PyClass>();
for (PyClassLikeType type : getAncestorTypes(context)) {
if (type instanceof PyClassType) {
results.add(((PyClassType)type).getPyClass());
}
}
return results;
}
public boolean isSubclass(PyClass parent) {
if (this == parent) {
return true;
}
for (PyClass superclass : iterateAncestorClasses()) {
for (PyClass superclass : getAncestorClasses()) {
if (parent == superclass) return true;
}
return false;
@@ -201,8 +224,10 @@ public class PyClassImpl extends PyPresentableElementImpl<PyClassStub> 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;
}
@@ -242,7 +267,7 @@ public class PyClassImpl extends PyPresentableElementImpl<PyClassStub> implement
if (slots != null) {
return slots;
}
for (PyClass cls : iterateAncestorClasses()) {
for (PyClass cls : getAncestorClasses()) {
slots = ((PyClassImpl)cls).getOwnSlots();
if (slots != null) {
return slots;
@@ -260,186 +285,89 @@ public class PyClassImpl extends PyPresentableElementImpl<PyClassStub> implement
return PyFileImpl.getStringListFromTargetExpression(PyNames.SLOTS, getClassAttributes());
}
protected List<PyClassRef> getSuperClassesList() {
if (PyNames.FAKE_OLD_BASE.equals(getName())) {
return Collections.emptyList();
}
List<PyClassRef> result = resolveSuperClassesFromStub();
if (result == null) {
result = new ArrayList<PyClassRef>();
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<PyClassRef> 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<PyClassRef> result = new ArrayList<PyClassRef>();
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<PyClassRef> pyClasses = resolveSuperClassesFromStub();
if (pyClasses == null) {
return EMPTY_ARRAY;
}
List<PyClass> result = new ArrayList<PyClass>();
for (PyClassRef clsRef : pyClasses) {
PyClass pyClass = clsRef.getPyClass();
if (pyClass != null) {
result.add(pyClass);
}
}
return result.toArray(new PyClass[result.size()]);
final List<PyClassLikeType> superTypes = getSuperClassTypes(TypeEvalContext.fastStubOnly(null));
if (superTypes.isEmpty()) {
return EMPTY_ARRAY;
}
PsiElement[] superClassElements = getSuperClassElements();
if (superClassElements.length > 0) {
List<PyClass> result = new ArrayList<PyClass>();
for (PsiElement element : superClassElements) {
if (element instanceof PyClass) {
result.add((PyClass)element);
}
final List<PyClass> result = new ArrayList<PyClass>();
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()]);
}
public
@NotNull
List<PyClass> 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.<PyClass>emptyList());
}
private static List<PyClass> mroMerge(List<List<PyClass>> sequences) {
List<PyClass> result = new LinkedList<PyClass>(); // need to insert to 0th position on linearize
private static List<PyClassLikeType> mroMerge(@NotNull List<List<PyClassLikeType>> sequences) {
List<PyClassLikeType> result = new LinkedList<PyClassLikeType>(); // need to insert to 0th position on linearize
while (true) {
// filter blank sequences
List<List<PyClass>> nonBlankSequences = new ArrayList<List<PyClass>>(sequences.size());
for (List<PyClass> item : sequences) {
List<List<PyClassLikeType>> nonBlankSequences = new ArrayList<List<PyClassLikeType>>(sequences.size());
for (List<PyClassLikeType> 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<PyClass> seq : nonBlankSequences) {
boolean found = false;
PyClassLikeType head = null; // to keep compiler happy; really head is assigned in the loop at least once.
for (List<PyClassLikeType> seq : nonBlankSequences) {
head = seq.get(0);
boolean head_in_tails = false;
for (List<PyClass> tail_seq : nonBlankSequences) {
for (List<PyClassLikeType> tail_seq : nonBlankSequences) {
if (tail_seq.indexOf(head) > 0) { // -1 is not found, 0 is head, >0 is tail.
head_in_tails = true;
break;
}
}
if (!head_in_tails) {
found = true;
break;
}
else {
head = null; // as a signal
}
}
assert head != null : "Inconsistent hierarchy!"; // TODO: better diagnostics?
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<PyClass> seq : nonBlankSequences) {
if (seq.get(0) == head) seq.remove(0);
for (List<PyClassLikeType> 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<PyClass> mroLinearize(PyClass cls, List<PyClass> seen) {
assert (seen.indexOf(cls) < 0) : "Circular import structure on " + PyUtil.nvl(cls);
PyClass[] bases = cls.getSuperClasses();
List<List<PyClass>> lins = new ArrayList<List<PyClass>>(bases.length * 2);
ArrayList<PyClass> new_seen = new ArrayList<PyClass>(seen.size() + 1);
new_seen.add(cls);
for (PyClass base : bases) {
List<PyClass> lin = mroLinearize(base, new_seen);
if (!lin.isEmpty()) lins.add(lin);
@NotNull
private static List<PyClassLikeType> mroLinearize(@NotNull PyClassLikeType type, @NotNull Set<PyClassLikeType> seen, boolean addThisType,
@NotNull TypeEvalContext context) {
if (seen.contains(type)) {
throw new IllegalStateException("Circular class inheritance");
}
for (PyClass base : bases) {
lins.add(new SmartList<PyClass>(base));
final List<PyClassLikeType> bases = type.getSuperClassTypes(context);
List<List<PyClassLikeType>> lines = new ArrayList<List<PyClassLikeType>>();
for (PyClassLikeType base : bases) {
if (base != null) {
final Set<PyClassLikeType> newSeen = new HashSet<PyClassLikeType>(seen);
newSeen.add(type);
List<PyClassLikeType> lin = mroLinearize(base, newSeen, true, context);
if (!lin.isEmpty()) lines.add(lin);
}
}
if (!bases.isEmpty()) {
lines.add(bases);
}
List<PyClassLikeType> result = mroMerge(lines);
if (addThisType) {
result.add(0, type);
}
List<PyClass> result = mroMerge(lins);
result.add(0, cls);
return result;
}
@@ -662,7 +590,7 @@ public class PyClassImpl extends PyPresentableElementImpl<PyClassStub> 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;
@@ -734,7 +662,7 @@ public class PyClassImpl extends PyPresentableElementImpl<PyClassStub> 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;
@@ -826,7 +754,7 @@ public class PyClassImpl extends PyPresentableElementImpl<PyClassStub> 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;
}
@@ -842,7 +770,7 @@ public class PyClassImpl extends PyPresentableElementImpl<PyClassStub> 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;
}
@@ -855,7 +783,7 @@ public class PyClassImpl extends PyPresentableElementImpl<PyClassStub> implement
List<PyTargetExpression> 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;
}
@@ -910,7 +838,7 @@ public class PyClassImpl extends PyPresentableElementImpl<PyClassStub> implement
}
}
if (inherited) {
for (PyClass ancestor : iterateAncestorClasses()) {
for (PyClass ancestor : getAncestorClasses()) {
final PyTargetExpression attribute = ancestor.findInstanceAttribute(name, false);
if (attribute != null) {
return attribute;
@@ -997,15 +925,17 @@ public class PyClassImpl extends PyPresentableElementImpl<PyClassStub> 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 : getOldStyleAncestorTypes(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;
@@ -1121,140 +1051,148 @@ public class PyClassImpl extends PyPresentableElementImpl<PyClassStub> implement
return super.getUseScope();
}
private static class AncestorsIterable implements Iterable<PyClassRef> {
private final PyClassImpl myClass;
public AncestorsIterable(final PyClassImpl pyClass) {
myClass = pyClass;
@NotNull
@Override
public List<PyClassLikeType> getSuperClassTypes(@NotNull TypeEvalContext context) {
if (PyNames.FAKE_OLD_BASE.equals(getName())) {
return Collections.emptyList();
}
final PyClassStub stub = getStub();
final List<PyClassLikeType> result = new ArrayList<PyClassLikeType>();
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 PyClassLikeType ? (PyClassLikeType)type : null);
}
}
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 PyClassLikeType) {
result.add((PyClassLikeType)type);
}
}
}
return result;
}
public Iterator<PyClassRef> iterator() {
return new AncestorsIterator(myClass);
@NotNull
@Override
public List<PyClassLikeType> getAncestorTypes(@NotNull TypeEvalContext context) {
return calculateAncestorTypes(context);
}
@NotNull
private List<PyClassLikeType> 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);
}
}
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<PyClassRef>();
@NotNull
private List<PyClassLikeType> getMROAncestorTypes(@NotNull TypeEvalContext context) {
final PyType thisType = context.getType(this);
if (thisType instanceof PyClassLikeType) {
try {
return mroLinearize((PyClassLikeType)thisType, new HashSet<PyClassLikeType>(), false, context);
}
catch (IllegalStateException ignored) {
}
}
return Collections.emptyList();
}
private AncestorsIterator(PyClassImpl AClass, Set<PyClassRef> seen) {
myAClass = AClass;
this.seen = seen;
percolator = myAClass.getSuperClassesList().iterator();
@NotNull
private List<PyClassLikeType> getOldStyleAncestorTypes(@NotNull TypeEvalContext context) {
final List<PyClassLikeType> results = new ArrayList<PyClassLikeType>();
final List<PyClassLikeType> toProcess = new ArrayList<PyClassLikeType>();
final Set<PyClassLikeType> seen = new HashSet<PyClassLikeType>();
final Set<PyClassLikeType> visited = new HashSet<PyClassLikeType>();
final PyType thisType = context.getType(this);
if (thisType instanceof PyClassLikeType) {
toProcess.add((PyClassLikeType)thisType);
}
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;
while (!toProcess.isEmpty()) {
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 (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;
if (superType != null && !visited.contains(superType)) {
toProcess.add(superType);
}
}
}
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 {
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;
definer = null;
break;
}
}
}
public void remove() {
throw new UnsupportedOperationException();
if (definer != null) {
return definer.getElementNamed(name);
}
return null;
}
}
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;
@Nullable
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 PyClassLikeType) {
return (PyClassLikeType)type;
}
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();
}
return null;
}
}
@@ -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(myContext.getTypeEvalContext())) {
init = ancestor.findMethodByName(PyNames.INIT, false);
if (init != null) {
// add to results as low priority
@@ -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) {
@@ -18,7 +18,7 @@ public class PySuperMethodsSearch extends ExtensibleQueryFactory<PsiElement, PyS
private static PyFunction getBaseMethod(List<PsiElement> 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;
@@ -19,7 +19,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.iterateAncestorClasses()) {
for (PyClass superClass : containingClass.getAncestorClasses()) {
if (!queryParameters.isDeepSearch()) {
boolean isAlreadyFound = false;
for (PyClass alreadyFound : foundMethodContainingClasses) {
@@ -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<PyClassLikeType> getSuperClassTypes(@NotNull TypeEvalContext context) {
return myClass.getSuperClassTypes(context);
}
@Nullable
public List<? extends RatedResolveResult> resolveMember(@NotNull final String name,
@Nullable PyExpression location,
@@ -117,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);
@@ -142,14 +148,14 @@ 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<PyClass> base_it = derived_class.iterateAncestorClasses().iterator();
final Iterator<PyClass> base_it = derived_class.getAncestorClasses(context).iterator();
if (base_it.hasNext()) {
return new PyClassTypeImpl(base_it.next(), true).resolveMember(name, location, direction, resolveContext);
}
@@ -165,28 +171,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(context)) {
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<? extends RatedResolveResult> 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) {
@@ -203,18 +202,19 @@ 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);
}
}
}
}
return Collections.emptyList();
}
@@ -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<String> ourPossibleFields = ImmutableSet.of("__name__", "__file__", "__path__", "__doc__", "__dict__", "__package__");
public static final ImmutableSet<String> 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<String> getPossibleInstanceMembers() {
return ourPossibleFields;
return MODULE_MEMBERS;
}
}
@@ -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)) {
@@ -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<String> 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<String> 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;
}
}
@@ -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;
}
@@ -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();
@@ -0,0 +1,5 @@
from CircularInheritance2 import B
class Foo(B):
pass
@@ -0,0 +1,9 @@
from CircularInheritance import Foo
class X(object):
pass
class B(X, Foo):
pass
@@ -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
@@ -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
@@ -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
@@ -0,0 +1,10 @@
class F(object):
pass
class E(F):
pass
class G(F, E):
pass
@@ -0,0 +1,10 @@
class F(object):
pass
class E(F):
pass
class G(E, F):
pass
@@ -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
@@ -0,0 +1,10 @@
class B1(object):
pass
class B2(object):
pass
class C(B1, B2):
pass
@@ -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)
@@ -1,8 +0,0 @@
<?xml version="1.0" encoding="UTF-8"?>
<problems>
<problem>
<file>test.py</file>
<line>7</line>
<description>Exception doesn't inherit from base 'Exception' class</description>
</problem>
</problems>
@@ -0,0 +1,21 @@
def test_basic():
class A(object):
pass
class C(A):
pass
raise <warning descr="Exception doesn't inherit from base 'Exception' class">C()</warning>
class D(C, Exception):
pass
raise D()
# PY-5811
def test_unknown_type_exception():
Unknown = None
class C(Unknown): pass
raise C()
@@ -0,0 +1 @@
<error descr="Unresolved reference '__init__'">__init__</error>
@@ -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.getAncestorClasses().iterator().next();
assertNotNull(cls);
assertNotParsed(file);
}
@@ -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,
@@ -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() {
@@ -0,0 +1,82 @@
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");
}
// 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<PyClassLikeType> types = cls.getAncestorTypes(TypeEvalContext.fastStubOnly(null));
final List<String> classNames = new ArrayList<String>();
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";
}
}
@@ -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 {
@@ -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);