mirror of
https://gitflic.ru/project/openide/openide.git
synced 2026-09-27 10:03:11 +07:00
Merge branch 'new-mro'
Conflicts: python/src/META-INF/python-plugin-common.xml
This commit is contained in:
@@ -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(),
|
||||
|
||||
+1
-1
@@ -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());
|
||||
|
||||
+1
-1
@@ -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
-2
@@ -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
|
||||
+18
@@ -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 {
|
||||
|
||||
+5
@@ -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);
|
||||
|
||||
Reference in New Issue
Block a user