From de5f5f0f765a631141d0334cc75e40954833bf6f Mon Sep 17 00:00:00 2001 From: "Ilya.Kazakevich" Date: Mon, 6 Oct 2014 21:37:51 +0400 Subject: [PATCH] PY-13740: Django 1.7 QuerySet.as_manager() code completion support --- ...DynamicMember.java => PyCustomMember.java} | 39 ++-- .../src/com/jetbrains/python/psi/PyClass.java | 14 +- .../com/jetbrains/python/psi/PyFunction.java | 46 +++++ .../psi/types/PyClassMembersProvider.java | 4 +- .../psi/types/PyClassMembersProviderBase.java | 10 +- .../psi/types/PyModuleMembersProvider.java | 8 +- .../jetbrains/python/psi/types/PyType.java | 4 +- .../python/psi/types/PyTypeProviderBase.java | 1 + .../NumpyModuleMembersProvider.java | 8 +- .../com/jetbrains/python/PyBundle.properties | 6 +- .../jetbrains/python/PyCustomMembersType.java | 189 ++++++++++++++++++ .../codeInsight/PyCustomMemberUtils.java | 34 ++++ .../PyMethodNavigationOffsetProvider.java | 2 +- .../PySuperMethodCompletionContributor.java | 4 +- .../override/PyOverrideImplementUtil.java | 2 +- .../stdlib/PyStdlibClassMembersProvider.java | 16 +- .../stdlib/PyStdlibModuleMembersProvider.java | 8 +- .../PyUserSkeletonsClassMembersProvider.java | 16 +- .../PyUserSkeletonsModuleMembersProvider.java | 8 +- .../PyOldStyleClassesInspection.java | 2 +- .../quickfix/AddFieldQuickFix.java | 2 +- .../PyUnresolvedReferencesInspection.java | 32 ++- .../python/projectView/PyElementNode.java | 2 +- .../com/jetbrains/python/psi/PsiQuery.java | 23 ++- .../python/psi/impl/PyClassImpl.java | 44 +++- .../python/psi/impl/PyEvaluator.java | 9 + .../python/psi/impl/PyFunctionImpl.java | 36 ++++ .../python/psi/types/PyClassTypeImpl.java | 21 +- .../python/psi/types/PyModuleType.java | 4 +- .../membersManager/MethodsManager.java | 4 +- .../extractmethod/PyExtractMethodUtil.java | 2 +- .../PythonAtTestConfigurationProducer.java | 2 +- .../testing/doctest/PythonDocTestUtil.java | 2 +- .../python/testing/pytest/PyTestUtil.java | 2 +- .../python/PyControlFlowBuilderTest.java | 4 +- .../com/jetbrains/python/PyOverrideTest.java | 8 +- .../com/jetbrains/python/PyStubsTest.java | 10 +- .../classes/PyDependenciesComparatorTest.java | 3 +- 38 files changed, 508 insertions(+), 123 deletions(-) rename python/psi-api/src/com/jetbrains/python/codeInsight/{PyDynamicMember.java => PyCustomMember.java} (80%) create mode 100644 python/src/com/jetbrains/python/PyCustomMembersType.java create mode 100644 python/src/com/jetbrains/python/codeInsight/PyCustomMemberUtils.java diff --git a/python/psi-api/src/com/jetbrains/python/codeInsight/PyDynamicMember.java b/python/psi-api/src/com/jetbrains/python/codeInsight/PyCustomMember.java similarity index 80% rename from python/psi-api/src/com/jetbrains/python/codeInsight/PyDynamicMember.java rename to python/psi-api/src/com/jetbrains/python/codeInsight/PyCustomMember.java index a344e892588c..ffc846796806 100644 --- a/python/psi-api/src/com/jetbrains/python/codeInsight/PyDynamicMember.java +++ b/python/psi-api/src/com/jetbrains/python/codeInsight/PyCustomMember.java @@ -33,7 +33,7 @@ import javax.swing.*; /** * @author Dennis.Ushakov */ -public class PyDynamicMember { +public class PyCustomMember { private String myName; private final boolean myResolveToInstance; private final Function myTypeCallback; @@ -45,7 +45,7 @@ public class PyDynamicMember { boolean myFunction = false; - public PyDynamicMember(@NotNull final String name, @Nullable final String type, final boolean resolveToInstance) { + public PyCustomMember(@NotNull final String name, @Nullable final String type, final boolean resolveToInstance) { myName = name; myResolveToInstance = resolveToInstance; myTypeName = type; @@ -54,7 +54,7 @@ public class PyDynamicMember { myTypeCallback = null; } - public PyDynamicMember(@NotNull final String name) { + public PyCustomMember(@NotNull final String name) { myName = name; myResolveToInstance = false; myTypeName = null; @@ -63,9 +63,9 @@ public class PyDynamicMember { myTypeCallback = null; } - public PyDynamicMember(@NotNull final String name, - @Nullable final String type, - final Function typeCallback) { + public PyCustomMember(@NotNull final String name, + @Nullable final String type, + final Function typeCallback) { myName = name; myResolveToInstance = false; @@ -75,55 +75,58 @@ public class PyDynamicMember { myTypeCallback = typeCallback; } - public PyDynamicMember(@NotNull final String name, @Nullable final PsiElement target) { + public PyCustomMember(@NotNull final String name, @Nullable final PsiElement target, @Nullable String typeName) { myName = name; myTarget = target; myResolveToInstance = false; - myTypeName = null; + myTypeName = typeName; myTypeCallback = null; } + public PyCustomMember(@NotNull final String name, @Nullable final PsiElement target) { + this(name, target, null); + } - public PyDynamicMember resolvesTo(String moduleQName) { + public PyCustomMember resolvesTo(String moduleQName) { myPsiPath = new PyPsiPath.ToFile(moduleQName); return this; } - public PyDynamicMember resolvesToClass(String classQName) { + public PyCustomMember resolvesToClass(String classQName) { myPsiPath = new PyPsiPath.ToClassQName(classQName); return this; } - public PyDynamicMember toClass(String name) { + public PyCustomMember toClass(String name) { myPsiPath = new PyPsiPath.ToClass(myPsiPath, name); return this; } - public PyDynamicMember toFunction(String name) { + public PyCustomMember toFunction(String name) { myPsiPath = new PyPsiPath.ToFunction(myPsiPath, name); return this; } - public PyDynamicMember toFunctionRecursive(String name) { + public PyCustomMember toFunctionRecursive(String name) { myPsiPath = new PyPsiPath.ToFunctionRecursive(myPsiPath, name); return this; } - public PyDynamicMember toClassAttribute(String name) { + public PyCustomMember toClassAttribute(String name) { myPsiPath = new PyPsiPath.ToClassAttribute(myPsiPath, name); return this; } - public PyDynamicMember toCall(String name, String... args) { + public PyCustomMember toCall(String name, String... args) { myPsiPath = new PyPsiPath.ToCall(myPsiPath, name, args); return this; } - public PyDynamicMember toAssignment(String assignee) { + public PyCustomMember toAssignment(String assignee) { myPsiPath = new PyPsiPath.ToAssignment(myPsiPath, assignee); return this; } - public PyDynamicMember toPsiElement(final PsiElement psiElement) { + public PyCustomMember toPsiElement(final PsiElement psiElement) { myPsiPath = new PyPsiPath() { @Override @@ -179,7 +182,7 @@ public class PyDynamicMember { return myTypeName.substring(pos + 1); } - public PyDynamicMember asFunction() { + public PyCustomMember asFunction() { myFunction = true; return this; } diff --git a/python/psi-api/src/com/jetbrains/python/psi/PyClass.java b/python/psi-api/src/com/jetbrains/python/psi/PyClass.java index a988714c144a..b6a26fc35539 100644 --- a/python/psi-api/src/com/jetbrains/python/psi/PyClass.java +++ b/python/psi-api/src/com/jetbrains/python/psi/PyClass.java @@ -110,8 +110,12 @@ public interface PyClass extends PsiNameIdentifierOwner, PyStatement, NameDefine @NotNull PyExpression[] getSuperClassExpressions(); + /** + * @param inherited return inherited (parent) methods as well + * @return class methods + */ @NotNull - PyFunction[] getMethods(); + PyFunction[] getMethods(boolean inherited); /** * Get class properties. @@ -243,4 +247,12 @@ public interface PyClass extends PsiNameIdentifierOwner, PyStatement, NameDefine */ @Nullable PyExpression getMetaClassExpression(); + + /** + * + * @param context eval context + * @return {@link com.jetbrains.python.psi.types.PyType} casted if it has right type + */ + @Nullable + PyClassLikeType getType(@NotNull TypeEvalContext context); } diff --git a/python/psi-api/src/com/jetbrains/python/psi/PyFunction.java b/python/psi-api/src/com/jetbrains/python/psi/PyFunction.java index e70a7ad12a51..0f7031b85dc2 100644 --- a/python/psi-api/src/com/jetbrains/python/psi/PyFunction.java +++ b/python/psi-api/src/com/jetbrains/python/psi/PyFunction.java @@ -26,6 +26,8 @@ import com.jetbrains.python.psi.types.PyType; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; +import java.util.List; + /** * Function declaration in source (the def and everything within). * @@ -101,4 +103,48 @@ extends @Nullable PyAnnotation getAnnotation(); + + // TODO: Doc + // + + /** + * Searches for function attributes. + * See PEP-0232 + * @return assignment statements for function attributes + */ + @NotNull + List findAttributes(); + + /** + * @return function protection level (underscore based) + */ + @NotNull + ProtectionLevel getProtectionLevel(); + + enum ProtectionLevel { + /** + * public members + */ + PUBLIC(0), + /** + * _protected_memebers + */ + PROTECTED(1), + /** + * __private_memebrs + */ + PRIVATE(2); + private final int myUnderscoreLevel; + + ProtectionLevel(final int underscoreLevel) { + myUnderscoreLevel = underscoreLevel; + } + + /** + * @return number of underscores + */ + public int getUnderscoreLevel() { + return myUnderscoreLevel; + } + } } diff --git a/python/psi-api/src/com/jetbrains/python/psi/types/PyClassMembersProvider.java b/python/psi-api/src/com/jetbrains/python/psi/types/PyClassMembersProvider.java index e051b48fbfaa..6a61de999abc 100644 --- a/python/psi-api/src/com/jetbrains/python/psi/types/PyClassMembersProvider.java +++ b/python/psi-api/src/com/jetbrains/python/psi/types/PyClassMembersProvider.java @@ -17,7 +17,7 @@ package com.jetbrains.python.psi.types; import com.intellij.openapi.extensions.ExtensionPointName; import com.intellij.psi.PsiElement; -import com.jetbrains.python.codeInsight.PyDynamicMember; +import com.jetbrains.python.codeInsight.PyCustomMember; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; @@ -30,7 +30,7 @@ public interface PyClassMembersProvider { ExtensionPointName EP_NAME = ExtensionPointName.create("Pythonid.pyClassMembersProvider"); @NotNull - Collection getMembers(PyClassType clazz, @Nullable PsiElement location); + Collection getMembers(PyClassType clazz, @Nullable PsiElement location); @Nullable PsiElement resolveMember(PyClassType clazz, String name, @Nullable PsiElement location); diff --git a/python/psi-api/src/com/jetbrains/python/psi/types/PyClassMembersProviderBase.java b/python/psi-api/src/com/jetbrains/python/psi/types/PyClassMembersProviderBase.java index a7d66499cf52..6ec96559911a 100644 --- a/python/psi-api/src/com/jetbrains/python/psi/types/PyClassMembersProviderBase.java +++ b/python/psi-api/src/com/jetbrains/python/psi/types/PyClassMembersProviderBase.java @@ -16,7 +16,7 @@ package com.jetbrains.python.psi.types; import com.intellij.psi.PsiElement; -import com.jetbrains.python.codeInsight.PyDynamicMember; +import com.jetbrains.python.codeInsight.PyCustomMember; import com.jetbrains.python.psi.PyClass; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; @@ -30,22 +30,22 @@ import java.util.Collections; public class PyClassMembersProviderBase implements PyClassMembersProvider { @NotNull @Override - public Collection getMembers(PyClassType clazz, PsiElement location) { + public Collection getMembers(PyClassType clazz, PsiElement location) { return Collections.emptyList(); } @Override public PsiElement resolveMember(PyClassType clazz, String name, PsiElement location) { - final Collection members = getMembers(clazz, location); + final Collection members = getMembers(clazz, location); return resolveMemberByName(members, clazz, name); } @Nullable - public static PsiElement resolveMemberByName(Collection members, + public static PsiElement resolveMemberByName(Collection members, PyClassType clazz, String name) { final PyClass pyClass = clazz.getPyClass(); - for (PyDynamicMember member : members) { + for (PyCustomMember member : members) { if (member.getName().equals(name)) { return member.resolve(pyClass); } diff --git a/python/psi-api/src/com/jetbrains/python/psi/types/PyModuleMembersProvider.java b/python/psi-api/src/com/jetbrains/python/psi/types/PyModuleMembersProvider.java index 696dcaee6623..60d13fde976e 100644 --- a/python/psi-api/src/com/jetbrains/python/psi/types/PyModuleMembersProvider.java +++ b/python/psi-api/src/com/jetbrains/python/psi/types/PyModuleMembersProvider.java @@ -18,7 +18,7 @@ package com.jetbrains.python.psi.types; import com.intellij.openapi.extensions.ExtensionPointName; import com.intellij.openapi.vfs.VirtualFile; import com.intellij.psi.PsiElement; -import com.jetbrains.python.codeInsight.PyDynamicMember; +import com.jetbrains.python.codeInsight.PyCustomMember; import com.jetbrains.python.psi.PyFile; import com.jetbrains.python.psi.PyPsiFacade; import com.jetbrains.python.psi.resolve.PointInImport; @@ -33,7 +33,7 @@ import java.util.Collections; public abstract class PyModuleMembersProvider { public static final ExtensionPointName EP_NAME = ExtensionPointName.create("Pythonid.pyModuleMembersProvider"); - public Collection getMembers(PyFile module, PointInImport point) { + public Collection getMembers(PyFile module, PointInImport point) { final VirtualFile vFile = module.getVirtualFile(); if (vFile != null) { final String qName = PyPsiFacade.getInstance(module.getProject()).findShortestImportableName(vFile, module); @@ -46,7 +46,7 @@ public abstract class PyModuleMembersProvider { @Nullable public PsiElement resolveMember(PyFile module, String name) { - for (PyDynamicMember o : getMembers(module, PointInImport.NONE)) { + for (PyCustomMember o : getMembers(module, PointInImport.NONE)) { if (o.getName().equals(name)) { return o.resolve(module); } @@ -54,5 +54,5 @@ public abstract class PyModuleMembersProvider { return null; } - protected abstract Collection getMembersByQName(PyFile module, String qName); + protected abstract Collection getMembersByQName(PyFile module, String qName); } diff --git a/python/psi-api/src/com/jetbrains/python/psi/types/PyType.java b/python/psi-api/src/com/jetbrains/python/psi/types/PyType.java index 67ba765bbf81..4640fceb324d 100644 --- a/python/psi-api/src/com/jetbrains/python/psi/types/PyType.java +++ b/python/psi-api/src/com/jetbrains/python/psi/types/PyType.java @@ -47,8 +47,8 @@ public interface PyType { * or a list of elements that define the name, a la multiResolve(). */ @Nullable - List resolveMember(@NotNull final String name, @Nullable PyExpression location, - @NotNull AccessDirection direction, @NotNull PyResolveContext resolveContext); + List resolveMember(@NotNull String name, @Nullable final PyExpression location, + @NotNull final AccessDirection direction, @NotNull final PyResolveContext resolveContext); /** * Proposes completion variants from type's attributes. diff --git a/python/psi-api/src/com/jetbrains/python/psi/types/PyTypeProviderBase.java b/python/psi-api/src/com/jetbrains/python/psi/types/PyTypeProviderBase.java index 0c60e6555b59..320397c4c12a 100644 --- a/python/psi-api/src/com/jetbrains/python/psi/types/PyTypeProviderBase.java +++ b/python/psi-api/src/com/jetbrains/python/psi/types/PyTypeProviderBase.java @@ -80,6 +80,7 @@ public class PyTypeProviderBase implements PyTypeProvider { } }; + @Nullable @Override public PyType getReferenceExpressionType(@NotNull PyReferenceExpression referenceExpression, @NotNull TypeEvalContext context) { return null; diff --git a/python/src/com/jetbrains/numpy/codeInsight/NumpyModuleMembersProvider.java b/python/src/com/jetbrains/numpy/codeInsight/NumpyModuleMembersProvider.java index 1fd2e3afd58f..fadf38916d0b 100644 --- a/python/src/com/jetbrains/numpy/codeInsight/NumpyModuleMembersProvider.java +++ b/python/src/com/jetbrains/numpy/codeInsight/NumpyModuleMembersProvider.java @@ -15,7 +15,7 @@ */ package com.jetbrains.numpy.codeInsight; -import com.jetbrains.python.codeInsight.PyDynamicMember; +import com.jetbrains.python.codeInsight.PyCustomMember; import com.jetbrains.python.psi.PyFile; import com.jetbrains.python.psi.types.PyModuleMembersProvider; @@ -39,11 +39,11 @@ public class NumpyModuleMembersProvider extends PyModuleMembersProvider { }; @Override - protected Collection getMembersByQName(PyFile module, String qName) { + protected Collection getMembersByQName(PyFile module, String qName) { if ("numpy".equals(qName)) { - final List members = new ArrayList(); + final List members = new ArrayList(); for (String type : NUMERIC_TYPES) { - members.add(new PyDynamicMember(type, "numpy.core.multiarray.dtype", false)); + members.add(new PyCustomMember(type, "numpy.core.multiarray.dtype", false)); } return members; } diff --git a/python/src/com/jetbrains/python/PyBundle.properties b/python/src/com/jetbrains/python/PyBundle.properties index d82e9a6fde62..9e9c68f64a69 100644 --- a/python/src/com/jetbrains/python/PyBundle.properties +++ b/python/src/com/jetbrains/python/PyBundle.properties @@ -846,4 +846,8 @@ remote.interpreter.default.interpreter.path=/usr/bin/python remote.interpreter.unspecified.interpreter.path=Specify Python interpreter path remote.interpreter.unspecified.temp.files.path=Specify path for PyCharm helpers remote.interpreter.configure.path.label=Python interpreter path: -remote.interpreter.configure.temp.files.path.label=PyCharm helpers path: \ No newline at end of file +remote.interpreter.configure.temp.files.path.label=PyCharm helpers path: + +# Message we display for inspection if user uses custom class type members that do not exist +custom.type.name=Dynamic class +custom.type.mimic.name=Dynamic class based on {0} \ No newline at end of file diff --git a/python/src/com/jetbrains/python/PyCustomMembersType.java b/python/src/com/jetbrains/python/PyCustomMembersType.java new file mode 100644 index 000000000000..7e6b3b07f727 --- /dev/null +++ b/python/src/com/jetbrains/python/PyCustomMembersType.java @@ -0,0 +1,189 @@ +package com.jetbrains.python; + +import com.intellij.codeInsight.lookup.LookupElement; +import com.intellij.psi.PsiElement; +import com.intellij.util.ArrayUtil; +import com.intellij.util.ProcessingContext; +import com.jetbrains.python.codeInsight.PyCustomMember; +import com.jetbrains.python.codeInsight.PyCustomMemberUtils; +import com.jetbrains.python.psi.AccessDirection; +import com.jetbrains.python.psi.PyCallSiteExpression; +import com.jetbrains.python.psi.PyExpression; +import com.jetbrains.python.psi.resolve.PyResolveContext; +import com.jetbrains.python.psi.resolve.RatedResolveResult; +import com.jetbrains.python.psi.types.*; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; + +import java.util.*; + +/** + * Custom (aka dynamic) type that has some members you pass to it. To be used for cases like "type()" + * This class can also mimic any other class (optionally). When mimics, it has all methods from this class and its own. + * @author Ilya.Kazakevich + */ +public class PyCustomMembersType implements PyClassLikeType { + @NotNull + private final Map myMembers; + @Nullable + private final PyClassType myTypeToMimic; + + /** + * @param typeToMimic this type may mimic some other class-based type. Pass it to have all members from this class + custom. + * Check class manual for more info. + * @param members custom members + */ + public PyCustomMembersType(@Nullable final PyClassType typeToMimic, @NotNull final PyCustomMember... members) { + myTypeToMimic = typeToMimic; + + myMembers = new HashMap(members.length); + for (final PyCustomMember member : members) { + myMembers.put(member.getName(), member); + } + } + + /** + * @return class we mimic (if any). Check class manual for more info. + */ + @Nullable + public PyClassType getTypeToMimic() { + return myTypeToMimic; + } + + @Override + public boolean isDefinition() { + return false; + } + + @Override + public PyClassLikeType toInstance() { + return this; + } + + @Nullable + @Override + public String getClassQName() { + return null; + } + + @NotNull + @Override + public List getSuperClassTypes(@NotNull TypeEvalContext context) { + return Collections.emptyList(); + } + + @Nullable + @Override + public List resolveMember(@NotNull final String name, + @Nullable final PyExpression location, + @NotNull final AccessDirection direction, + @NotNull final PyResolveContext resolveContext, + final boolean inherited) { + if (myMembers.containsKey(name)) { + PsiElement context = null; + if (location != null) { + context = location; + } + if (context == null) { + context = resolveContext.getTypeEvalContext().getOrigin(); + } + if (context != null) { + final PsiElement resolveResult = myMembers.get(name).resolve(context); + if (resolveResult != null) { + return Collections.singletonList(new RatedResolveResult(0, resolveResult)); + } + } + } + if (myTypeToMimic != null) { + return myTypeToMimic.resolveMember(name, location, direction, resolveContext, inherited); + } + return null; + } + + @Override + public boolean isValid() { + return true; + } + + @Nullable + @Override + public PyClassLikeType getMetaClassType(@NotNull TypeEvalContext context, boolean inherited) { + return null; + } + + @Override + public boolean isCallable() { + return true; + } + + @Nullable + @Override + public PyType getReturnType(@NotNull TypeEvalContext context) { + return null; + } + + @Nullable + @Override + public PyType getCallType(@NotNull TypeEvalContext context, @NotNull PyCallSiteExpression callSite) { + return this; + } + + @Nullable + @Override + public List getParameters(@NotNull TypeEvalContext context) { + return null; + } + + @Nullable + @Override + public List resolveMember(@NotNull final String name, + @Nullable final PyExpression location, + @NotNull final AccessDirection direction, + @NotNull final PyResolveContext resolveContext) { + return resolveMember(name, location, direction, resolveContext, true); + } + + @Override + public Object[] getCompletionVariants(final String completionPrefix, final PsiElement location, final ProcessingContext context) { + final Collection lookupElements = new ArrayList(myMembers.size()); + for (final PyCustomMember member : myMembers.values()) { + lookupElements.add(PyCustomMemberUtils.toLookUpElement(member, member.getShortType())); + } + return ArrayUtil.mergeArrays(ArrayUtil.toObjectArray(lookupElements), + ((myTypeToMimic != null) + ? myTypeToMimic.getCompletionVariants(completionPrefix, location, context) + : PsiElement.EMPTY_ARRAY)); + } + + + @Nullable + @Override + public String getName() { + String mimicName = null; + if (myTypeToMimic != null) { + mimicName = myTypeToMimic.getName(); + } + if (mimicName != null) { + return PyBundle.message("custom.type.mimic.name", mimicName); + } + return PyBundle.message("custom.type.name"); + } + + @Override + public boolean isBuiltin() { + return false; + } + + @Override + public void assertValid(final String message) { + + } + + /** + * @param name name to check + * @return True if this class (not the one it mimics!) has member with passed name + */ + public boolean hasMember(@NotNull final String name) { + return myMembers.containsKey(name); + } +} diff --git a/python/src/com/jetbrains/python/codeInsight/PyCustomMemberUtils.java b/python/src/com/jetbrains/python/codeInsight/PyCustomMemberUtils.java new file mode 100644 index 000000000000..86da6a9326f5 --- /dev/null +++ b/python/src/com/jetbrains/python/codeInsight/PyCustomMemberUtils.java @@ -0,0 +1,34 @@ +package com.jetbrains.python.codeInsight; + +import com.intellij.codeInsight.completion.util.ParenthesesInsertHandler; +import com.intellij.codeInsight.lookup.LookupElementBuilder; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; + +/** + * TODO: Move methods to {@link com.jetbrains.python.codeInsight.PyCustomMember}. Only dependency hell prevents me from doing it + */ +public final class PyCustomMemberUtils { + private PyCustomMemberUtils() { + } + + /** + * Creates {@link com.intellij.codeInsight.lookup.LookupElement} to be used in cases like {@link com.jetbrains.python.psi.types.PyType#getCompletionVariants(String, com.intellij.psi.PsiElement, com.intellij.util.ProcessingContext)} + * This method should be in {@link com.jetbrains.python.codeInsight.PyCustomMember} but it does not. We need to move it. + * + * @param member custom member + * @param typeText type text (if any) + * @return lookup element + */ + @NotNull + public static LookupElementBuilder toLookUpElement(@NotNull final PyCustomMember member, @Nullable final String typeText) { + + LookupElementBuilder lookupElementBuilder = LookupElementBuilder.create(member.getName()) + .withIcon(member.getIcon()) + .withTypeText(typeText); + if (member.isFunction()) { + lookupElementBuilder = lookupElementBuilder.withInsertHandler(ParenthesesInsertHandler.NO_PARAMETERS).withLookupString("()"); + } + return lookupElementBuilder; + } +} diff --git a/python/src/com/jetbrains/python/codeInsight/PyMethodNavigationOffsetProvider.java b/python/src/com/jetbrains/python/codeInsight/PyMethodNavigationOffsetProvider.java index 045bdc1307b3..a00366ab5d7d 100644 --- a/python/src/com/jetbrains/python/codeInsight/PyMethodNavigationOffsetProvider.java +++ b/python/src/com/jetbrains/python/codeInsight/PyMethodNavigationOffsetProvider.java @@ -54,7 +54,7 @@ public class PyMethodNavigationOffsetProvider implements MethodNavigationOffsetP array.add(psiElement); } else if (psiElement instanceof PyClass) { - Collections.addAll(array, ((PyClass)psiElement).getMethods()); + Collections.addAll(array, ((PyClass)psiElement).getMethods(false)); } } } diff --git a/python/src/com/jetbrains/python/codeInsight/completion/PySuperMethodCompletionContributor.java b/python/src/com/jetbrains/python/codeInsight/completion/PySuperMethodCompletionContributor.java index 04ad5ae8e958..4778c6db1ccc 100644 --- a/python/src/com/jetbrains/python/codeInsight/completion/PySuperMethodCompletionContributor.java +++ b/python/src/com/jetbrains/python/codeInsight/completion/PySuperMethodCompletionContributor.java @@ -57,13 +57,13 @@ public class PySuperMethodCompletionContributor extends CompletionContributor { return; } Set seenNames = new HashSet(); - for (PyFunction function : containingClass.getMethods()) { + for (PyFunction function : containingClass.getMethods(false)) { seenNames.add(function.getName()); } LanguageLevel languageLevel = LanguageLevel.forElement(parameters.getOriginalFile()); seenNames.addAll(PyNames.getBuiltinMethods(languageLevel).keySet()); for (PyClass ancestor : containingClass.getAncestorClasses()) { - for (PyFunction superMethod : ancestor.getMethods()) { + for (PyFunction superMethod : ancestor.getMethods(false)) { if (!seenNames.contains(superMethod.getName())) { String text = superMethod.getName() + superMethod.getParameterList().getText(); LookupElementBuilder element = LookupElementBuilder.create(text); diff --git a/python/src/com/jetbrains/python/codeInsight/override/PyOverrideImplementUtil.java b/python/src/com/jetbrains/python/codeInsight/override/PyOverrideImplementUtil.java index 5a6caf0a8941..634eaf9a59c9 100644 --- a/python/src/com/jetbrains/python/codeInsight/override/PyOverrideImplementUtil.java +++ b/python/src/com/jetbrains/python/codeInsight/override/PyOverrideImplementUtil.java @@ -281,7 +281,7 @@ public class PyOverrideImplementUtil { public static Collection getAllSuperFunctions(@NotNull final PyClass pyClass) { final Map superFunctions = new HashMap(); for (PyClass aClass : pyClass.getAncestorClasses()) { - for (PyFunction function : aClass.getMethods()) { + for (PyFunction function : aClass.getMethods(false)) { if (!superFunctions.containsKey(function.getName())) { superFunctions.put(function.getName(), function); } diff --git a/python/src/com/jetbrains/python/codeInsight/stdlib/PyStdlibClassMembersProvider.java b/python/src/com/jetbrains/python/codeInsight/stdlib/PyStdlibClassMembersProvider.java index b0b8c06c92de..334552aaf8c0 100644 --- a/python/src/com/jetbrains/python/codeInsight/stdlib/PyStdlibClassMembersProvider.java +++ b/python/src/com/jetbrains/python/codeInsight/stdlib/PyStdlibClassMembersProvider.java @@ -17,7 +17,7 @@ package com.jetbrains.python.codeInsight.stdlib; import com.intellij.openapi.util.Key; import com.intellij.psi.PsiElement; -import com.jetbrains.python.codeInsight.PyDynamicMember; +import com.jetbrains.python.codeInsight.PyCustomMember; import com.jetbrains.python.psi.PyClass; import com.jetbrains.python.psi.PyFile; import com.jetbrains.python.psi.PyTargetExpression; @@ -35,16 +35,16 @@ import java.util.List; * @author yole */ public class PyStdlibClassMembersProvider extends PyClassMembersProviderBase { - private Key> mySocketMembersKey = Key.create("socket.members"); + private Key> mySocketMembersKey = Key.create("socket.members"); @NotNull @Override - public Collection getMembers(PyClassType classType, PsiElement location) { + public Collection getMembers(PyClassType classType, PsiElement location) { PyClass clazz = classType.getPyClass(); final String qualifiedName = clazz.getQualifiedName(); if ("socket._socketobject".equals(qualifiedName)) { final PyFile socketFile = (PyFile)clazz.getContainingFile(); - List socketMembers = socketFile.getUserData(mySocketMembersKey); + List socketMembers = socketFile.getUserData(mySocketMembersKey); if (socketMembers == null) { socketMembers = calcSocketMembers(socketFile); socketFile.putUserData(mySocketMembersKey, socketMembers); @@ -54,20 +54,20 @@ public class PyStdlibClassMembersProvider extends PyClassMembersProviderBase { return Collections.emptyList(); } - private static List calcSocketMembers(PyFile socketFile) { - List result = new ArrayList(); + private static List calcSocketMembers(PyFile socketFile) { + List result = new ArrayList(); addMethodsFromAttr(socketFile, result, "_socketmethods"); addMethodsFromAttr(socketFile, result, "_delegate_methods"); return result; } - private static void addMethodsFromAttr(PyFile socketFile, List result, final String attrName) { + private static void addMethodsFromAttr(PyFile socketFile, List result, final String attrName) { final PyTargetExpression socketMethods = socketFile.findTopLevelAttribute(attrName); if (socketMethods != null) { final List methods = PyUtil.getStringListFromTargetExpression(socketMethods); if (methods != null) { for (String name : methods) { - result.add(new PyDynamicMember(name).resolvesTo("_socket").toClass("SocketType").toFunction(name)); + result.add(new PyCustomMember(name).resolvesTo("_socket").toClass("SocketType").toFunction(name)); } } } diff --git a/python/src/com/jetbrains/python/codeInsight/stdlib/PyStdlibModuleMembersProvider.java b/python/src/com/jetbrains/python/codeInsight/stdlib/PyStdlibModuleMembersProvider.java index 80d3627ac083..a724b7808a75 100644 --- a/python/src/com/jetbrains/python/codeInsight/stdlib/PyStdlibModuleMembersProvider.java +++ b/python/src/com/jetbrains/python/codeInsight/stdlib/PyStdlibModuleMembersProvider.java @@ -18,7 +18,7 @@ package com.jetbrains.python.codeInsight.stdlib; import com.intellij.openapi.util.SystemInfo; import com.intellij.psi.PsiElement; import com.intellij.psi.util.QualifiedName; -import com.jetbrains.python.codeInsight.PyDynamicMember; +import com.jetbrains.python.codeInsight.PyCustomMember; import com.jetbrains.python.psi.PyFile; import com.jetbrains.python.psi.resolve.ResolveImportUtil; import com.jetbrains.python.psi.types.PyModuleMembersProvider; @@ -33,15 +33,15 @@ import java.util.List; */ public class PyStdlibModuleMembersProvider extends PyModuleMembersProvider { @Override - protected Collection getMembersByQName(PyFile module, String qName) { + protected Collection getMembersByQName(PyFile module, String qName) { if (qName.equals("os")) { - final List results = new ArrayList(); + final List results = new ArrayList(); PsiElement path = null; if (module != null) { final String pathModuleName = SystemInfo.isWindows ? "ntpath" : "posixpath"; path = ResolveImportUtil.resolveModuleInRoots(QualifiedName.fromDottedString(pathModuleName), module); } - results.add(new PyDynamicMember("path", path)); + results.add(new PyCustomMember("path", path)); return results; } return Collections.emptyList(); diff --git a/python/src/com/jetbrains/python/codeInsight/userSkeletons/PyUserSkeletonsClassMembersProvider.java b/python/src/com/jetbrains/python/codeInsight/userSkeletons/PyUserSkeletonsClassMembersProvider.java index 5ac7fc10751c..bcc39aab9010 100644 --- a/python/src/com/jetbrains/python/codeInsight/userSkeletons/PyUserSkeletonsClassMembersProvider.java +++ b/python/src/com/jetbrains/python/codeInsight/userSkeletons/PyUserSkeletonsClassMembersProvider.java @@ -16,7 +16,7 @@ package com.jetbrains.python.codeInsight.userSkeletons; import com.intellij.psi.PsiElement; -import com.jetbrains.python.codeInsight.PyDynamicMember; +import com.jetbrains.python.codeInsight.PyCustomMember; import com.jetbrains.python.psi.PyClass; import com.jetbrains.python.psi.PyFunction; import com.jetbrains.python.psi.PyTargetExpression; @@ -37,7 +37,7 @@ import java.util.List; public class PyUserSkeletonsClassMembersProvider extends PyClassMembersProviderBase implements PyOverridingAncestorsClassMembersProvider { @NotNull @Override - public Collection getMembers(@NotNull PyClassType classType, PsiElement location) { + public Collection getMembers(@NotNull PyClassType classType, PsiElement location) { final PyClass cls = classType.getPyClass(); final PyClass skeleton = PyUserSkeletonsUtil.getUserSkeleton(cls); if (skeleton != null) { @@ -73,24 +73,24 @@ public class PyUserSkeletonsClassMembersProvider extends PyClassMembersProviderB return null; } - private static Collection getClassMembers(@NotNull PyClass cls) { - final List result = new ArrayList(); - for (PyFunction function : cls.getMethods()) { + private static Collection getClassMembers(@NotNull PyClass cls) { + final List result = new ArrayList(); + for (PyFunction function : cls.getMethods(false)) { final String name = function.getName(); if (name != null) { - result.add(new PyDynamicMember(name, function)); + result.add(new PyCustomMember(name, function)); } } for (PyTargetExpression attribute : cls.getInstanceAttributes()) { final String name = attribute.getName(); if (name != null) { - result.add(new PyDynamicMember(name, attribute)); + result.add(new PyCustomMember(name, attribute)); } } for (PyTargetExpression attribute : cls.getClassAttributes()) { final String name = attribute.getName(); if (name != null) { - result.add(new PyDynamicMember(name, attribute)); + result.add(new PyCustomMember(name, attribute)); } } return result; diff --git a/python/src/com/jetbrains/python/codeInsight/userSkeletons/PyUserSkeletonsModuleMembersProvider.java b/python/src/com/jetbrains/python/codeInsight/userSkeletons/PyUserSkeletonsModuleMembersProvider.java index ad876ee4ec91..c42d15c6f463 100644 --- a/python/src/com/jetbrains/python/codeInsight/userSkeletons/PyUserSkeletonsModuleMembersProvider.java +++ b/python/src/com/jetbrains/python/codeInsight/userSkeletons/PyUserSkeletonsModuleMembersProvider.java @@ -17,7 +17,7 @@ package com.jetbrains.python.codeInsight.userSkeletons; import com.intellij.psi.PsiElement; import com.intellij.psi.PsiFileSystemItem; -import com.jetbrains.python.codeInsight.PyDynamicMember; +import com.jetbrains.python.codeInsight.PyCustomMember; import com.jetbrains.python.psi.PyElement; import com.jetbrains.python.psi.PyFile; import com.jetbrains.python.psi.types.PyModuleMembersProvider; @@ -43,17 +43,17 @@ public class PyUserSkeletonsModuleMembersProvider extends PyModuleMembersProvide } @Override - protected Collection getMembersByQName(PyFile module, String qName) { + protected Collection getMembersByQName(PyFile module, String qName) { final PyFile moduleSkeleton = PyUserSkeletonsUtil.getUserSkeletonForModuleQName(qName, module); if (moduleSkeleton != null) { - final List results = new ArrayList(); + final List results = new ArrayList(); for (PyElement element : moduleSkeleton.iterateNames()) { if (element instanceof PsiFileSystemItem) { continue; } final String name = element.getName(); if (name != null) { - results.add(new PyDynamicMember(name, element)); + results.add(new PyCustomMember(name, element)); } } return results; diff --git a/python/src/com/jetbrains/python/inspections/PyOldStyleClassesInspection.java b/python/src/com/jetbrains/python/inspections/PyOldStyleClassesInspection.java index a253be86344f..0ea757dbebc1 100644 --- a/python/src/com/jetbrains/python/inspections/PyOldStyleClassesInspection.java +++ b/python/src/com/jetbrains/python/inspections/PyOldStyleClassesInspection.java @@ -74,7 +74,7 @@ public class PyOldStyleClassesInspection extends PyInspection { registerProblem(attr, "Old-style class contains __slots__ definition", ProblemHighlightType.GENERIC_ERROR_OR_WARNING, null, quickFixes.toArray(new LocalQuickFix[quickFixes.size()])); } } - for (PyFunction attr : node.getMethods()) { + for (PyFunction attr : node.getMethods(false)) { if ("__getattribute__".equals(attr.getName())) { final ASTNode nameNode = attr.getNameNode(); assert nameNode != null; diff --git a/python/src/com/jetbrains/python/inspections/quickfix/AddFieldQuickFix.java b/python/src/com/jetbrains/python/inspections/quickfix/AddFieldQuickFix.java index 8ecd8a5d9b03..367f22b0540c 100644 --- a/python/src/com/jetbrains/python/inspections/quickfix/AddFieldQuickFix.java +++ b/python/src/com/jetbrains/python/inspections/quickfix/AddFieldQuickFix.java @@ -166,7 +166,7 @@ public class AddFieldQuickFix implements LocalQuickFix { appendToMethod(newInit, callback); PsiElement addAnchor = null; - PyFunction[] meths = cls.getMethods(); + PyFunction[] meths = cls.getMethods(false); if (meths.length > 0) addAnchor = meths[0].getPrevSibling(); PyStatementList clsContent = cls.getStatementList(); newInit = (PyFunction) clsContent.addAfter(newInit, addAnchor); diff --git a/python/src/com/jetbrains/python/inspections/unresolvedReference/PyUnresolvedReferencesInspection.java b/python/src/com/jetbrains/python/inspections/unresolvedReference/PyUnresolvedReferencesInspection.java index 8049546a6e92..10b4c6f8705a 100644 --- a/python/src/com/jetbrains/python/inspections/unresolvedReference/PyUnresolvedReferencesInspection.java +++ b/python/src/com/jetbrains/python/inspections/unresolvedReference/PyUnresolvedReferencesInspection.java @@ -33,9 +33,10 @@ import com.intellij.psi.util.QualifiedName; import com.intellij.util.Consumer; import com.intellij.util.PlatformUtils; import com.jetbrains.python.PyBundle; +import com.jetbrains.python.PyCustomMembersType; import com.jetbrains.python.PyNames; import com.jetbrains.python.codeInsight.PyCodeInsightSettings; -import com.jetbrains.python.codeInsight.PyDynamicMember; +import com.jetbrains.python.codeInsight.PyCustomMember; import com.jetbrains.python.codeInsight.controlflow.ScopeOwner; import com.jetbrains.python.codeInsight.dataflow.scope.ScopeUtil; import com.jetbrains.python.codeInsight.imports.AutoImportHintAction; @@ -462,7 +463,7 @@ public class PyUnresolvedReferencesInspection extends PyInspection { } if (expr.isQualified()) { final PyClassTypeImpl object_type = (PyClassTypeImpl)PyBuiltinCache.getInstance(node).getObjectType(); - if ((object_type != null) && object_type.getPossibleInstanceMembers().contains(refName)){ + if ((object_type != null) && object_type.getPossibleInstanceMembers().contains(refName)) { return; } } @@ -542,6 +543,10 @@ public class PyUnresolvedReferencesInspection extends PyInspection { } markedQualified = true; } + else if (isHasCustomMember(refName, type)) { + // We have dynamic members + return; + } else { description = PyBundle.message("INSP.cannot.find.$0.in.$1", refText, type.getName()); markedQualified = true; @@ -605,6 +610,16 @@ public class PyUnresolvedReferencesInspection extends PyInspection { registerProblem(node, description, hl_type, null, rangeInElement, actions.toArray(new LocalQuickFix[actions.size()])); } + /** + * Checks if type is custom-member based and has custom member with certain name + * @param refName name to check + * @param type type + * @return true if has one + */ + private static boolean isHasCustomMember(@NotNull final String refName, @NotNull final PyType type) { + return (type instanceof PyCustomMembersType) && ((PyCustomMembersType)type).hasMember(refName); + } + /** * Return the canonical qualified name for a reference (even for an unresolved one). */ @@ -687,6 +702,13 @@ public class PyUnresolvedReferencesInspection extends PyInspection { return true; } } + if (type instanceof PyCustomMembersType) { + // Skip custom member types that mimics another class with fuzzy parents + PyClassType mimic = ((PyCustomMembersType)type).getTypeToMimic(); + if (mimic != null && PyUtil.hasUnresolvedAncestors(mimic.getPyClass(), myTypeEvalContext)) { + return true; + } + } if (type instanceof PyClassTypeImpl) { PyClass cls = ((PyClassType)type).getPyClass(); if (overridesGetAttr(cls, myTypeEvalContext)) { @@ -721,8 +743,8 @@ public class PyUnresolvedReferencesInspection extends PyInspection { PsiReference reference, @NotNull final String name) { for (PyClassMembersProvider provider : Extensions.getExtensions(PyClassMembersProvider.EP_NAME)) { - final Collection resolveResult = provider.getMembers(type, reference.getElement()); - for (PyDynamicMember member : resolveResult) { + final Collection resolveResult = provider.getMembers(type, reference.getElement()); + for (PyCustomMember member : resolveResult) { if (member.getName().equals(name)) return true; } } @@ -810,7 +832,7 @@ public class PyUnresolvedReferencesInspection extends PyInspection { } } } - for (PyFunction method : containedClass.getMethods()) { + for (PyFunction method : containedClass.getMethods(false)) { if (expr.getText().equals(method.getName())) { actions.add(new UnresolvedReferenceAddSelfQuickFix(expr, qualifier)); } diff --git a/python/src/com/jetbrains/python/projectView/PyElementNode.java b/python/src/com/jetbrains/python/projectView/PyElementNode.java index 6ec347236773..3ef8ddd71a6b 100644 --- a/python/src/com/jetbrains/python/projectView/PyElementNode.java +++ b/python/src/com/jetbrains/python/projectView/PyElementNode.java @@ -50,7 +50,7 @@ public class PyElementNode extends BasePsiNode { for (PyClass aClass : pyClass.getNestedClasses()) { result.add(new PyElementNode(myProject, aClass, getSettings())); } - for (PyFunction function : pyClass.getMethods()) { + for (PyFunction function : pyClass.getMethods(false)) { result.add(new PyElementNode(myProject, function, getSettings())); } return result; diff --git a/python/src/com/jetbrains/python/psi/PsiQuery.java b/python/src/com/jetbrains/python/psi/PsiQuery.java index d41f6ba68a2c..3e1e26de12c3 100644 --- a/python/src/com/jetbrains/python/psi/PsiQuery.java +++ b/python/src/com/jetbrains/python/psi/PsiQuery.java @@ -172,9 +172,7 @@ public class PsiQuery { } } } - @SuppressWarnings("unchecked") // Type is preserved - final T[] array = (T[])result.toArray(new PsiElement[result.size()]); - return new PsiTypedQuery(clazz, array); + return new PsiTypedQuery(clazz, result); } @@ -309,14 +307,17 @@ public class PsiQuery { * Filter elements by class */ @NotNull - public PsiQuery filter(@NotNull final Class clazz) { + public PsiTypedQuery filter(@NotNull final Class clazz) { final Set result = new HashSet(Arrays.asList(myPsiElements)); for (final PsiElement element : myPsiElements) { - if (PyUtil.as(element, clazz) == null) { + if (!(clazz.isInstance(element))) { result.remove(element); } } - return new PsiQuery(result.toArray(new PsiElement[result.size()])); + // We checked it in runtime + @SuppressWarnings("unchecked") + final List toAdd = (List)new ArrayList(result); + return new PsiTypedQuery(clazz, toAdd); } @@ -351,16 +352,16 @@ public class PsiQuery { @NotNull private final Class myClass; @NotNull - private final T[] myElements; + private final List myElements; /** * @param clazz type * @param elements elements */ - private PsiTypedQuery(@NotNull final Class clazz, @NotNull final T... elements) { + private PsiTypedQuery(@NotNull final Class clazz, @NotNull final List elements) { super(elements); myClass = clazz; - myElements = elements.clone(); + myElements = elements; } /** @@ -383,8 +384,8 @@ public class PsiQuery { * @return All elements of certain type */ @NotNull - public T[] getElements() { - return myElements.clone(); + public List getElements() { + return Collections.unmodifiableList(myElements); } } } diff --git a/python/src/com/jetbrains/python/psi/impl/PyClassImpl.java b/python/src/com/jetbrains/python/psi/impl/PyClassImpl.java index 611cbc84ad2e..c7f2173973c0 100644 --- a/python/src/com/jetbrains/python/psi/impl/PyClassImpl.java +++ b/python/src/com/jetbrains/python/psi/impl/PyClassImpl.java @@ -389,9 +389,34 @@ public class PyClassImpl extends PyBaseElementImpl implements PyCla return result; } + @Override @NotNull - public PyFunction[] getMethods() { - return getClassChildren(PythonDialectsTokenSetProvider.INSTANCE.getFunctionDeclarationTokens(), PyFunction.ARRAY_FACTORY); + public PyFunction[] getMethods(final boolean inherited) { + final PyFunction[] thisClassFunctions = + getClassChildren(PythonDialectsTokenSetProvider.INSTANCE.getFunctionDeclarationTokens(), PyFunction.ARRAY_FACTORY); + if (!inherited) { + return thisClassFunctions; + } + // Map to get rid of duplicated (overwritten methods) + final Map result = new HashMap(); + // We get classes in MRO order (hopefully), so last methods are last + for (final PyClass superClass : getSuperClasses()) { + for (final PyFunction function : superClass.getMethods(false)) { + final String functionName = function.getName(); + if (functionName != null) { + result.put(functionName, function); + } + } + } + // We now need to add our own methods + for (final PyFunction function : thisClassFunctions) { + final String functionName = function.getName(); + if (functionName != null) { + result.put(functionName, function); + } + } + final Collection functionsToReturn = result.values(); + return functionsToReturn.toArray(new PyFunction[functionsToReturn.size()]); } @Override @@ -514,7 +539,7 @@ public class PyClassImpl extends PyBaseElementImpl implements PyCla // look at @property decorators Map> grouped = new HashMap>(); // group suitable same-named methods, each group defines a property - for (PyFunction method : getMethods()) { + for (PyFunction method : getMethods(false)) { final String methodName = method.getName(); if (name == null || name.equals(methodName)) { List bucket = grouped.get(methodName); @@ -826,7 +851,7 @@ public class PyClassImpl extends PyBaseElementImpl implements PyCla public boolean visitMethods(Processor processor, boolean inherited, boolean skipClassObj) { - PyFunction[] methods = getMethods(); + PyFunction[] methods = getMethods(false); if (!ContainerUtil.process(methods, processor)) return false; if (inherited) { for (PyClass ancestor : getAncestorClasses()) { @@ -932,7 +957,7 @@ public class PyClassImpl extends PyBaseElementImpl implements PyCla collectInstanceAttributes(initMethod, result); } Set namesInInit = new HashSet(result.keySet()); - final PyFunction[] methods = getMethods(); + final PyFunction[] methods = getMethods(false); for (PyFunction method : methods) { if (!PyNames.INIT.equals(method.getName())) { collectInstanceAttributes(method, result, namesInInit); @@ -981,7 +1006,8 @@ public class PyClassImpl extends PyBaseElementImpl implements PyCla final List result = new ArrayList(); statementList.accept(new PyRecursiveElementVisitor() { @Override - public void visitPyClass(PyClass node) {} + public void visitPyClass(PyClass node) { + } public void visitPyAssignmentStatement(final PyAssignmentStatement node) { for (PyExpression expression : node.getTargets()) { @@ -1338,4 +1364,10 @@ public class PyClassImpl extends PyBaseElementImpl implements PyCla } return null; } + + @Nullable + @Override + public PyClassLikeType getType(@NotNull TypeEvalContext context) { + return PyUtil.as(context.getType(this), PyClassLikeType.class); + } } diff --git a/python/src/com/jetbrains/python/psi/impl/PyEvaluator.java b/python/src/com/jetbrains/python/psi/impl/PyEvaluator.java index dea9296f12c6..3f948c496592 100644 --- a/python/src/com/jetbrains/python/psi/impl/PyEvaluator.java +++ b/python/src/com/jetbrains/python/psi/impl/PyEvaluator.java @@ -65,6 +65,15 @@ public class PyEvaluator { if (expr instanceof PySequenceExpression) { return evaluateSequenceExpression((PySequenceExpression)expr); } + if (expr instanceof PyQualifiedExpression) { // support bool + final String referencedName = ((PyQualifiedExpression)expr).getReferencedName(); + if (PyNames.TRUE.equals(referencedName)) { + return true; + } + if (PyNames.FALSE.equals(referencedName)) { + return true; + } + } if (expr instanceof PyCallExpression) { return evaluateCall((PyCallExpression)expr); } diff --git a/python/src/com/jetbrains/python/psi/impl/PyFunctionImpl.java b/python/src/com/jetbrains/python/psi/impl/PyFunctionImpl.java index 2c1842f3337f..9f6afb5e43f7 100644 --- a/python/src/com/jetbrains/python/psi/impl/PyFunctionImpl.java +++ b/python/src/com/jetbrains/python/psi/impl/PyFunctionImpl.java @@ -23,6 +23,7 @@ import com.intellij.openapi.util.Ref; import com.intellij.openapi.util.text.StringUtil; import com.intellij.openapi.vfs.VirtualFile; import com.intellij.psi.PsiElement; +import com.intellij.psi.PsiReference; import com.intellij.psi.StubBasedPsiElement; import com.intellij.psi.search.LocalSearchScope; import com.intellij.psi.search.SearchScope; @@ -699,4 +700,39 @@ public class PyFunctionImpl extends PyBaseElementImpl implements public String getQualifiedName() { return QualifiedNameFinder.getQualifiedName(this); } + + @NotNull + @Override + public List findAttributes() { + final List result = new ArrayList(); + for (final PyAssignmentStatement statement : new PsiQuery(this).siblings(PyAssignmentStatement.class).getElements()) { + for (final PyQualifiedExpression targetExpression : new PsiQuery(statement.getTargets()).filter(PyQualifiedExpression.class) + .getElements()) { + final PyExpression qualifier = targetExpression.getQualifier(); + if (qualifier == null) { + continue; + } + final PsiReference qualifierReference = qualifier.getReference(); + if (qualifierReference == null) { + continue; + } + if (qualifierReference.isReferenceTo(this)) { + result.add(statement); + } + } + } + return result; + } + + @NotNull + @Override + public ProtectionLevel getProtectionLevel() { + final int underscoreLevels = PyUtil.getInitialUnderscores(getName()); + for (final ProtectionLevel level : ProtectionLevel.values()) { + if (level.getUnderscoreLevel() == underscoreLevels) { + return level; + } + } + return ProtectionLevel.PRIVATE; + } } diff --git a/python/src/com/jetbrains/python/psi/types/PyClassTypeImpl.java b/python/src/com/jetbrains/python/psi/types/PyClassTypeImpl.java index 34a92e16bf01..d9b69121aa50 100644 --- a/python/src/com/jetbrains/python/psi/types/PyClassTypeImpl.java +++ b/python/src/com/jetbrains/python/psi/types/PyClassTypeImpl.java @@ -16,7 +16,6 @@ package com.jetbrains.python.psi.types; import com.intellij.codeInsight.completion.CompletionUtil; -import com.intellij.codeInsight.completion.util.ParenthesesInsertHandler; import com.intellij.codeInsight.lookup.LookupElement; import com.intellij.codeInsight.lookup.LookupElementBuilder; import com.intellij.openapi.extensions.Extensions; @@ -33,7 +32,8 @@ import com.intellij.util.ArrayUtil; import com.intellij.util.ProcessingContext; import com.intellij.util.containers.ContainerUtil; import com.jetbrains.python.PyNames; -import com.jetbrains.python.codeInsight.PyDynamicMember; +import com.jetbrains.python.codeInsight.PyCustomMember; +import com.jetbrains.python.codeInsight.PyCustomMemberUtils; import com.jetbrains.python.psi.*; import com.jetbrains.python.psi.impl.PyBuiltinCache; import com.jetbrains.python.psi.impl.ResolveResultList; @@ -151,7 +151,8 @@ public class PyClassTypeImpl extends UserDataHolderBase implements PyClassType { @NotNull PyResolveContext resolveContext, boolean inherited) { final TypeEvalContext context = resolveContext.getTypeEvalContext(); - PsiElement classMember = resolveByOverridingMembersProviders(this, name, location); //overriding members provers have priority to normal resolve + PsiElement classMember = + resolveByOverridingMembersProviders(this, name, location); //overriding members provers have priority to normal resolve if (classMember != null) { return ResolveResultList.to(classMember); } @@ -219,7 +220,8 @@ public class PyClassTypeImpl extends UserDataHolderBase implements PyClassType { } if (inherited) { - classMember = resolveByMembersProviders(this, name, location); //ask providers after real class introspection as providers have less priority + classMember = + resolveByMembersProviders(this, name, location); //ask providers after real class introspection as providers have less priority } if (classMember != null) { @@ -414,16 +416,11 @@ public class PyClassTypeImpl extends UserDataHolderBase implements PyClassType { addInheritedMembers(prefix, location, namesAlready, context, ret, typeEvalContext); // from providers - for (PyClassMembersProvider provider : Extensions.getExtensions(PyClassMembersProvider.EP_NAME)) { - for (PyDynamicMember member : provider.getMembers(this, location)) { + for (final PyClassMembersProvider provider : Extensions.getExtensions(PyClassMembersProvider.EP_NAME)) { + for (final PyCustomMember member : provider.getMembers(this, location)) { final String name = member.getName(); if (!namesAlready.contains(name)) { - LookupElementBuilder lookupElementBuilder = LookupElementBuilder.create(name).withIcon(member.getIcon()).withTypeText(getName()); - if (member.isFunction()) { - lookupElementBuilder = lookupElementBuilder.withInsertHandler(ParenthesesInsertHandler.NO_PARAMETERS); - lookupElementBuilder.withTailText("()"); - } - ret.add(lookupElementBuilder); + ret.add(PyCustomMemberUtils.toLookUpElement(member, getName())); } } } diff --git a/python/src/com/jetbrains/python/psi/types/PyModuleType.java b/python/src/com/jetbrains/python/psi/types/PyModuleType.java index db5a9ba3f1ae..0aec8612c989 100644 --- a/python/src/com/jetbrains/python/psi/types/PyModuleType.java +++ b/python/src/com/jetbrains/python/psi/types/PyModuleType.java @@ -30,7 +30,7 @@ import com.intellij.psi.scope.PsiScopeProcessor; import com.intellij.psi.util.PsiTreeUtil; import com.intellij.util.ProcessingContext; import com.jetbrains.python.PyNames; -import com.jetbrains.python.codeInsight.PyDynamicMember; +import com.jetbrains.python.codeInsight.PyCustomMember; import com.jetbrains.python.codeInsight.controlflow.ScopeOwner; import com.jetbrains.python.codeInsight.dataflow.scope.ScopeUtil; import com.jetbrains.python.psi.*; @@ -289,7 +289,7 @@ public class PyModuleType implements PyType { // Modules don't descend from obje Set namesAlready = context.get(CTX_NAMES); PointInImport point = ResolveImportUtil.getPointInImport(location); for (PyModuleMembersProvider provider : Extensions.getExtensions(PyModuleMembersProvider.EP_NAME)) { - for (PyDynamicMember member : provider.getMembers(myModule, point)) { + for (PyCustomMember member : provider.getMembers(myModule, point)) { final String name = member.getName(); if (namesAlready != null) { namesAlready.add(name); diff --git a/python/src/com/jetbrains/python/refactoring/classes/membersManager/MethodsManager.java b/python/src/com/jetbrains/python/refactoring/classes/membersManager/MethodsManager.java index 52c32d95d38d..56bd80ca3077 100644 --- a/python/src/com/jetbrains/python/refactoring/classes/membersManager/MethodsManager.java +++ b/python/src/com/jetbrains/python/refactoring/classes/membersManager/MethodsManager.java @@ -44,7 +44,7 @@ class MethodsManager extends MembersManager { @Override public boolean hasConflict(@NotNull final PyFunction member, @NotNull final PyClass aClass) { - return NamePredicate.hasElementWithSameName(member, Arrays.asList(aClass.getMethods())); + return NamePredicate.hasElementWithSameName(member, Arrays.asList(aClass.getMethods(false))); } @NotNull @@ -64,7 +64,7 @@ class MethodsManager extends MembersManager { @NotNull @Override protected List getMembersCouldBeMoved(@NotNull final PyClass pyClass) { - return FluentIterable.from(Arrays.asList(pyClass.getMethods())).filter(new NamelessFilter()).filter(NO_PROPERTIES).toList(); + return FluentIterable.from(Arrays.asList(pyClass.getMethods(false))).filter(new NamelessFilter()).filter(NO_PROPERTIES).toList(); } @Override diff --git a/python/src/com/jetbrains/python/refactoring/extractmethod/PyExtractMethodUtil.java b/python/src/com/jetbrains/python/refactoring/extractmethod/PyExtractMethodUtil.java index d16712dea6c7..a396a354a381 100644 --- a/python/src/com/jetbrains/python/refactoring/extractmethod/PyExtractMethodUtil.java +++ b/python/src/com/jetbrains/python/refactoring/extractmethod/PyExtractMethodUtil.java @@ -181,7 +181,7 @@ public class PyExtractMethodUtil { scope.add(owner); final PyClass containingClass = ((PyFunction)owner).getContainingClass(); if (containingClass != null) { - for (PyFunction function : containingClass.getMethods()) { + for (PyFunction function : containingClass.getMethods(false)) { if (!function.equals(owner) && !function.equals(generatedMethod)) scope.add(function); } diff --git a/python/src/com/jetbrains/python/testing/attest/PythonAtTestConfigurationProducer.java b/python/src/com/jetbrains/python/testing/attest/PythonAtTestConfigurationProducer.java index d0a6d19e19e2..006749a25e96 100644 --- a/python/src/com/jetbrains/python/testing/attest/PythonAtTestConfigurationProducer.java +++ b/python/src/com/jetbrains/python/testing/attest/PythonAtTestConfigurationProducer.java @@ -60,7 +60,7 @@ public class PythonAtTestConfigurationProducer extends } private static boolean hasTestFunction(@NotNull final PyClass pyClass) { - PyFunction[] methods = pyClass.getMethods(); + PyFunction[] methods = pyClass.getMethods(false); for (PyFunction function : methods) { PyDecoratorList decorators = function.getDecoratorList(); if (decorators == null) continue; diff --git a/python/src/com/jetbrains/python/testing/doctest/PythonDocTestUtil.java b/python/src/com/jetbrains/python/testing/doctest/PythonDocTestUtil.java index fd6e9ef357af..b0889b72c46d 100644 --- a/python/src/com/jetbrains/python/testing/doctest/PythonDocTestUtil.java +++ b/python/src/com/jetbrains/python/testing/doctest/PythonDocTestUtil.java @@ -58,7 +58,7 @@ public class PythonDocTestUtil { } public static boolean isDocTestClass(PyClass pyClass) { - for (PyFunction cls : pyClass.getMethods()) { + for (PyFunction cls : pyClass.getMethods(false)) { if (isDocTestFunction(cls)) { return true; } diff --git a/python/src/com/jetbrains/python/testing/pytest/PyTestUtil.java b/python/src/com/jetbrains/python/testing/pytest/PyTestUtil.java index b6f463b15706..357227532727 100644 --- a/python/src/com/jetbrains/python/testing/pytest/PyTestUtil.java +++ b/python/src/com/jetbrains/python/testing/pytest/PyTestUtil.java @@ -80,7 +80,7 @@ public class PyTestUtil { if (className == null) return false; final String name = className.toLowerCase(); if (name.startsWith("test")) { - for (PyFunction cls : pyClass.getMethods()) { + for (PyFunction cls : pyClass.getMethods(false)) { if (isPyTestFunction(cls)) { return true; } diff --git a/python/testSrc/com/jetbrains/python/PyControlFlowBuilderTest.java b/python/testSrc/com/jetbrains/python/PyControlFlowBuilderTest.java index 203b462c5a44..c5e489c06485 100644 --- a/python/testSrc/com/jetbrains/python/PyControlFlowBuilderTest.java +++ b/python/testSrc/com/jetbrains/python/PyControlFlowBuilderTest.java @@ -205,7 +205,7 @@ public class PyControlFlowBuilderTest extends LightMarkedTestCase { configureByFile(testName + ".py"); final String fullPath = getTestDataPath() + testName + ".txt"; final PyClass pyClass = ((PyFile) myFile).getTopLevelClasses().get(0); - final ControlFlow flow = ControlFlowCache.getControlFlow(pyClass.getMethods()[0]); + final ControlFlow flow = ControlFlowCache.getControlFlow(pyClass.getMethods(false)[0]); check(fullPath, flow); } @@ -214,7 +214,7 @@ public class PyControlFlowBuilderTest extends LightMarkedTestCase { configureByFile(testName + ".py"); final String fullPath = getTestDataPath() + testName + ".txt"; final PyClass pyClass = ((PyFile) myFile).getTopLevelClasses().get(0); - final ControlFlow flow = ControlFlowCache.getControlFlow(pyClass.getMethods()[0]); + final ControlFlow flow = ControlFlowCache.getControlFlow(pyClass.getMethods(false)[0]); check(fullPath, flow); } diff --git a/python/testSrc/com/jetbrains/python/PyOverrideTest.java b/python/testSrc/com/jetbrains/python/PyOverrideTest.java index d80dc06182e7..fddb9e26c905 100644 --- a/python/testSrc/com/jetbrains/python/PyOverrideTest.java +++ b/python/testSrc/com/jetbrains/python/PyOverrideTest.java @@ -35,7 +35,7 @@ import java.util.Collections; public class PyOverrideTest extends PyTestCase { private void doTest() { myFixture.configureByFile("override/" + getTestName(true) + ".py"); - PyFunction toOverride = getTopLevelClass(0).getMethods() [0]; + PyFunction toOverride = getTopLevelClass(0).getMethods(false) [0]; PyOverrideImplementUtil.overrideMethods(myFixture.getEditor(), getTopLevelClass(1), Collections.singletonList(new PyMethodMember(toOverride)), false); myFixture.checkResultByFile("override/" + getTestName(true) + "_after.py", true); @@ -86,7 +86,7 @@ public class PyOverrideTest extends PyTestCase { public void testInnerClass() { // PY-10976 myFixture.configureByFile("override/" + getTestName(true) + ".py"); - PyFunction toOverride = getTopLevelClass(0).getMethods()[0]; + PyFunction toOverride = getTopLevelClass(0).getMethods(false)[0]; PyClass pyClass = getTopLevelClass(1).getNestedClasses()[0]; PyOverrideImplementUtil.overrideMethods(myFixture.getEditor(), pyClass, Collections.singletonList(new PyMethodMember(toOverride)), false); @@ -95,7 +95,7 @@ public class PyOverrideTest extends PyTestCase { public void testInnerFunctionClass() { myFixture.configureByFile("override/" + getTestName(true) + ".py"); - PyFunction toOverride = getTopLevelClass(0).getMethods()[0]; + PyFunction toOverride = getTopLevelClass(0).getMethods(false)[0]; final PsiElement element = myFixture.getElementAtCaret(); PyOverrideImplementUtil.overrideMethods(myFixture.getEditor(), PsiTreeUtil.getParentOfType(element, PyClass.class, false), Collections.singletonList(new PyMethodMember(toOverride)), false); @@ -115,7 +115,7 @@ public class PyOverrideTest extends PyTestCase { public void testImplement() { myFixture.configureByFile("override/" + getTestName(true) + ".py"); - PyFunction toImplement = getTopLevelClass(0).getMethods()[1]; + PyFunction toImplement = getTopLevelClass(0).getMethods(false)[1]; PyOverrideImplementUtil.overrideMethods(myFixture.getEditor(), getTopLevelClass(1), Collections.singletonList(new PyMethodMember(toImplement)), true); myFixture.checkResultByFile("override/" + getTestName(true) + "_after.py", true); diff --git a/python/testSrc/com/jetbrains/python/PyStubsTest.java b/python/testSrc/com/jetbrains/python/PyStubsTest.java index 0904b4c3d7d0..2a58613f1668 100644 --- a/python/testSrc/com/jetbrains/python/PyStubsTest.java +++ b/python/testSrc/com/jetbrains/python/PyStubsTest.java @@ -75,7 +75,7 @@ public class PyStubsTest extends PyTestCase { assertEquals("staticField", attrs.get(0).getName()); assertTrue(attrs.get(0).getAssignedQName().matches("deco")); - final PyFunction[] methods = pyClass.getMethods(); + final PyFunction[] methods = pyClass.getMethods(false); assertEquals(2, methods.length); assertEquals("__init__", methods [0].getName()); assertEquals("fooFunction", methods [1].getName()); @@ -122,7 +122,7 @@ public class PyStubsTest extends PyTestCase { Property prop = pyClass.findProperty("value", true); Maybe maybe_function = prop.getGetter(); assertTrue(maybe_function.isDefined()); - assertEquals(pyClass.getMethods()[0], maybe_function.value()); + assertEquals(pyClass.getMethods(false)[0], maybe_function.value()); Property setvalueProp = pyClass.findProperty("setvalue", true); Maybe setter = setvalueProp.getSetter(); @@ -135,10 +135,10 @@ public class PyStubsTest extends PyTestCase { prop = pyClass.findProperty("x", true); maybe_function = prop.getGetter(); assertTrue(maybe_function.isDefined()); - assertEquals(pyClass.getMethods()[0], maybe_function.value()); + assertEquals(pyClass.getMethods(false)[0], maybe_function.value()); maybe_function = prop.getSetter(); assertTrue(maybe_function.isDefined()); - assertEquals(pyClass.getMethods()[1], maybe_function.value()); + assertEquals(pyClass.getMethods(false)[1], maybe_function.value()); // ...and the juice: assertNotParsed(file); @@ -342,7 +342,7 @@ public class PyStubsTest extends PyTestCase { public void testWrappedStaticMethod() { final PyFileImpl file = (PyFileImpl) getTestFile(); final PyClass pyClass = file.getTopLevelClasses().get(0); - final PyFunction[] methods = pyClass.getMethods(); + final PyFunction[] methods = pyClass.getMethods(false); assertEquals(1, methods.length); final PyFunction.Modifier modifier = methods[0].getModifier(); assertEquals(PyFunction.Modifier.STATICMETHOD, modifier); diff --git a/python/testSrc/com/jetbrains/python/refactoring/classes/PyDependenciesComparatorTest.java b/python/testSrc/com/jetbrains/python/refactoring/classes/PyDependenciesComparatorTest.java index 9ebe01b127dc..b3be3825f970 100644 --- a/python/testSrc/com/jetbrains/python/refactoring/classes/PyDependenciesComparatorTest.java +++ b/python/testSrc/com/jetbrains/python/refactoring/classes/PyDependenciesComparatorTest.java @@ -1,7 +1,6 @@ package com.jetbrains.python.refactoring.classes; import com.intellij.psi.PsiElement; -import com.intellij.util.text.Matcher; import com.jetbrains.python.fixtures.PyTestCase; import com.jetbrains.python.psi.*; import org.hamcrest.Matchers; @@ -23,7 +22,7 @@ public class PyDependenciesComparatorTest extends PyTestCase { @SuppressWarnings("ConstantConditions") // Can't be null (class has docstring) PsiElement docStringExpression = clazz.getDocStringExpression().getParent(); - PyFunction method = clazz.getMethods()[0]; + PyFunction method = clazz.getMethods(false)[0]; PsiElement classField = clazz.getClassAttributes().get(0).getParent(); final List elementList = new ArrayList();