diff options
author | Tor Norbye <tnorbye@google.com> | 2014-08-21 00:31:02 +0000 |
---|---|---|
committer | Gerrit Code Review <noreply-gerritcodereview@google.com> | 2014-08-16 04:55:08 +0000 |
commit | 9cde0e3c015174898df8b8f3672185941fad4786 (patch) | |
tree | 80a55c7b59c38377216daaada4e8bc47b69ceb9a /python/src/com/jetbrains/python/psi/types/PyFunctionType.java | |
parent | 3b37877a2561bf9fbe072253a18688807d523505 (diff) | |
parent | d76e3920c56d37c942092b7dca20fcaded81c0a5 (diff) | |
download | idea-9cde0e3c015174898df8b8f3672185941fad4786.tar.gz |
Merge "Merge remote-tracking branch 'aosp/upstream-master' into merge"
Diffstat (limited to 'python/src/com/jetbrains/python/psi/types/PyFunctionType.java')
-rw-r--r-- | python/src/com/jetbrains/python/psi/types/PyFunctionType.java | 76 |
1 files changed, 70 insertions, 6 deletions
diff --git a/python/src/com/jetbrains/python/psi/types/PyFunctionType.java b/python/src/com/jetbrains/python/psi/types/PyFunctionType.java index 4b3adfd112f8..4bdbae69d83d 100644 --- a/python/src/com/jetbrains/python/psi/types/PyFunctionType.java +++ b/python/src/com/jetbrains/python/psi/types/PyFunctionType.java @@ -18,10 +18,12 @@ package com.jetbrains.python.psi.types; import com.intellij.psi.PsiElement; 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.psi.*; import com.jetbrains.python.psi.impl.PyBuiltinCache; import com.jetbrains.python.psi.resolve.PyResolveContext; +import com.jetbrains.python.psi.resolve.QualifiedResolveResult; import com.jetbrains.python.psi.resolve.RatedResolveResult; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; @@ -30,6 +32,9 @@ import java.util.ArrayList; import java.util.Collections; import java.util.List; +import static com.jetbrains.python.psi.PyFunction.Modifier.STATICMETHOD; +import static com.jetbrains.python.psi.PyUtil.as; + /** * Type of a particular function that is represented as a {@link Callable} in the PSI tree. * @@ -74,20 +79,79 @@ public class PyFunctionType implements PyCallableType { @Nullable PyExpression location, @NotNull AccessDirection direction, @NotNull PyResolveContext resolveContext) { - final PyClassTypeImpl functionType = PyBuiltinCache.getInstance(getCallable()).getObjectType(PyNames.FAKE_FUNCTION); - if (functionType == null) { + final PyClassType delegate = selectFakeType(location, resolveContext.getTypeEvalContext()); + if (delegate == null) { return Collections.emptyList(); } - return functionType.resolveMember(name, location, direction, resolveContext); + return delegate.resolveMember(name, location, direction, resolveContext); } @Override public Object[] getCompletionVariants(String completionPrefix, PsiElement location, ProcessingContext context) { - final PyClassTypeImpl functionType = PyBuiltinCache.getInstance(getCallable()).getObjectType(PyNames.FAKE_FUNCTION); - if (functionType == null) { + final TypeEvalContext typeEvalContext = TypeEvalContext.userInitiated(location.getContainingFile()); + final PyClassType delegate; + if (location instanceof PyReferenceExpression) { + delegate = selectFakeType(((PyReferenceExpression)location).getQualifier(), typeEvalContext); + } + else { + delegate = PyBuiltinCache.getInstance(getCallable()).getObjectType(PyNames.FAKE_FUNCTION); + } + if (delegate == null) { return ArrayUtil.EMPTY_OBJECT_ARRAY; } - return functionType.getCompletionVariants(completionPrefix, location, context); + return delegate.getCompletionVariants(completionPrefix, location, context); + } + + /** + * Select either {@link PyNames#FAKE_FUNCTION} or {@link PyNames#FAKE_METHOD} fake class depending on concrete reference used and + * language level. Will fallback to fake function type. + */ + @Nullable + private PyClassTypeImpl selectFakeType(@Nullable PyExpression location, @NotNull TypeEvalContext context) { + if (location instanceof PyReferenceExpression && isBoundMethodReference(((PyReferenceExpression)location), context)) { + return PyBuiltinCache.getInstance(getCallable()).getObjectType(PyNames.FAKE_METHOD); + } + return PyBuiltinCache.getInstance(getCallable()).getObjectType(PyNames.FAKE_FUNCTION); + } + + private boolean isBoundMethodReference(@NotNull PyReferenceExpression location, @NotNull TypeEvalContext context) { + final PyFunction function = as(getCallable(), PyFunction.class); + final boolean isNonStaticMethod = function != null && function.getContainingClass() != null && function.getModifier() != STATICMETHOD; + if (isNonStaticMethod) { + // In Python 2 unbound methods have __method fake type + if (LanguageLevel.forElement(location).isOlderThan(LanguageLevel.PYTHON30)) { + return true; + } + final PyExpression qualifier; + if (location.isQualified()) { + qualifier = location.getQualifier(); + } + else { + final PyResolveContext resolveContext = PyResolveContext.noImplicits().withTypeEvalContext(context); + final QualifiedResolveResult resolveResult = location.followAssignmentsChain(resolveContext); + final List<PyExpression> qualifiers = resolveResult.getQualifiers(); + qualifier = ContainerUtil.isEmpty(qualifiers) ? null : qualifiers.get(qualifiers.size() - 1); + } + if (qualifier != null) { + //noinspection ConstantConditions + final PyType qualifierType = PyTypeChecker.toNonWeakType(context.getType(qualifier), context); + if (isInstanceType(qualifierType)) { + return true; + } + else if (qualifierType instanceof PyUnionType) { + for (PyType type : ((PyUnionType)qualifierType).getMembers()) { + if (isInstanceType(type)) { + return true; + } + } + } + } + } + return false; + } + + private static boolean isInstanceType(@Nullable PyType type) { + return type instanceof PyClassType && !((PyClassType)type).isDefinition(); } @Override |