diff --git a/python/pluginSrc/com/jetbrains/python/psi/impl/PyJavaClassType.java b/python/pluginSrc/com/jetbrains/python/psi/impl/PyJavaClassType.java index 02176e32c287..a667bb69f849 100644 --- a/python/pluginSrc/com/jetbrains/python/psi/impl/PyJavaClassType.java +++ b/python/pluginSrc/com/jetbrains/python/psi/impl/PyJavaClassType.java @@ -89,7 +89,7 @@ public class PyJavaClassType implements PyClassLikeType { } @Override - public boolean isBuiltin(TypeEvalContext context) { + public boolean isBuiltin() { return false; // TODO: JDK's types could be considered built-in. } @@ -104,13 +104,19 @@ public class PyJavaClassType implements PyClassLikeType { @Nullable @Override - public PyType getCallType(@NotNull TypeEvalContext context, @Nullable PyQualifiedExpression callSite) { + public PyType getReturnType(@NotNull TypeEvalContext context) { if (myDefinition) { return new PyJavaClassType(myClass, false); } return null; } + @Nullable + @Override + public PyType getCallType(@NotNull TypeEvalContext context, @NotNull PyQualifiedExpression callSite) { + return getReturnType(context); + } + @Nullable @Override public List getParameters(@NotNull TypeEvalContext context) { diff --git a/python/pluginSrc/com/jetbrains/python/psi/impl/PyJavaMethodType.java b/python/pluginSrc/com/jetbrains/python/psi/impl/PyJavaMethodType.java index dbcc7e2fc8c9..6f0fd48e8742 100644 --- a/python/pluginSrc/com/jetbrains/python/psi/impl/PyJavaMethodType.java +++ b/python/pluginSrc/com/jetbrains/python/psi/impl/PyJavaMethodType.java @@ -52,10 +52,16 @@ public class PyJavaMethodType implements PyCallableType { @Nullable @Override - public PyType getCallType(@NotNull TypeEvalContext context, @Nullable PyQualifiedExpression callSite) { + public PyType getReturnType(@NotNull TypeEvalContext context) { return PyJavaTypeProvider.asPyType(myMethod.getReturnType()); } + @Nullable + @Override + public PyType getCallType(@NotNull TypeEvalContext context, @NotNull PyQualifiedExpression callSite) { + return getReturnType(context); + } + @Nullable @Override public List getParameters(@NotNull TypeEvalContext context) { @@ -84,7 +90,7 @@ public class PyJavaMethodType implements PyCallableType { } @Override - public boolean isBuiltin(TypeEvalContext context) { + public boolean isBuiltin() { return false; } diff --git a/python/pluginSrc/com/jetbrains/python/psi/impl/PyJavaPackageType.java b/python/pluginSrc/com/jetbrains/python/psi/impl/PyJavaPackageType.java index 2948f2108abb..6f891c02c1c4 100644 --- a/python/pluginSrc/com/jetbrains/python/psi/impl/PyJavaPackageType.java +++ b/python/pluginSrc/com/jetbrains/python/psi/impl/PyJavaPackageType.java @@ -31,7 +31,6 @@ 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.PyType; -import com.jetbrains.python.psi.types.TypeEvalContext; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; @@ -96,7 +95,7 @@ public class PyJavaPackageType implements PyType { } @Override - public boolean isBuiltin(TypeEvalContext context) { + public boolean isBuiltin() { return false; } diff --git a/python/psi-api/src/com/jetbrains/python/psi/Callable.java b/python/psi-api/src/com/jetbrains/python/psi/Callable.java index 23a5ef7870b7..a31197ec60c1 100644 --- a/python/psi-api/src/com/jetbrains/python/psi/Callable.java +++ b/python/psi-api/src/com/jetbrains/python/psi/Callable.java @@ -20,6 +20,8 @@ import com.jetbrains.python.psi.types.TypeEvalContext; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; +import java.util.Map; + /** * Something that can be called, passed parameters to, and return something back. @@ -34,10 +36,24 @@ public interface Callable extends PyTypedElement, PyQualifiedNameOwner { PyParameterList getParameterList(); /** - * @return the type of returned value. + * Returns the return type of the callable independent of a call site. */ @Nullable - PyType getReturnType(@NotNull TypeEvalContext context, @Nullable PyQualifiedExpression callSite); + PyType getReturnType(@NotNull TypeEvalContext context, @NotNull TypeEvalContext.Key key); + + /** + * Returns the type of the call to the callable. + */ + @Nullable + PyType getCallType(@NotNull TypeEvalContext context, @NotNull PyQualifiedExpression callSite); + + /** + * Returns the type of the call to the callable where the call site is specified by the optional receiver and the arguments to parameters + * mapping. + */ + @Nullable + PyType getCallType(@Nullable PyExpression receiver, @NotNull Map parameters, + @NotNull TypeEvalContext context); /** * @return a methods returns itself, non-method callables return null. diff --git a/python/psi-api/src/com/jetbrains/python/psi/impl/PyTypeProvider.java b/python/psi-api/src/com/jetbrains/python/psi/impl/PyTypeProvider.java index ff188f56c262..5707005026cb 100644 --- a/python/psi-api/src/com/jetbrains/python/psi/impl/PyTypeProvider.java +++ b/python/psi-api/src/com/jetbrains/python/psi/impl/PyTypeProvider.java @@ -39,7 +39,10 @@ public interface PyTypeProvider { PyType getParameterType(@NotNull PyNamedParameter param, @NotNull PyFunction func, @NotNull TypeEvalContext context); @Nullable - PyType getReturnType(@NotNull PyFunction function, @Nullable PyQualifiedExpression callSite, @NotNull TypeEvalContext context); + PyType getReturnType(@NotNull Callable callable, @NotNull TypeEvalContext context); + + @Nullable + PyType getCallType(@NotNull PyFunction function, @Nullable PyQualifiedExpression callSite, @NotNull TypeEvalContext context); @Nullable PyType getContextManagerVariableType(PyClass contextManager, PyExpression withExpression, TypeEvalContext context); diff --git a/python/psi-api/src/com/jetbrains/python/psi/types/PyCallableType.java b/python/psi-api/src/com/jetbrains/python/psi/types/PyCallableType.java index 43261ddba84e..66fb827babe4 100644 --- a/python/psi-api/src/com/jetbrains/python/psi/types/PyCallableType.java +++ b/python/psi-api/src/com/jetbrains/python/psi/types/PyCallableType.java @@ -34,14 +34,19 @@ public interface PyCallableType extends PyType { boolean isCallable(); /** - * Returns the type which is the result of calling an instance of this type. + * Returns the return type of a function independent of a call site. * - * @return the call result type or null if invalid. + * For example, it may return a generic type. * @param context - * @param callSite */ @Nullable - PyType getCallType(@NotNull TypeEvalContext context, @Nullable PyQualifiedExpression callSite); + PyType getReturnType(@NotNull TypeEvalContext context); + + /** + * Returns the type which is the result of calling an instance of this type. + */ + @Nullable + PyType getCallType(@NotNull TypeEvalContext context, @NotNull PyQualifiedExpression callSite); /** * Returns the list of parameter types. 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 098761020989..67ba765bbf81 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 @@ -73,9 +73,8 @@ public interface PyType { /** * @return true if the type is a known built-in type. - * @param context */ - boolean isBuiltin(TypeEvalContext context); + boolean isBuiltin(); void assertValid(String message); } 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 6330c4a76a17..c27dc4bc04ed 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 @@ -94,8 +94,14 @@ public class PyTypeProviderBase implements PyTypeProvider { return null; } + @Nullable @Override - public PyType getReturnType(@NotNull PyFunction function, @Nullable PyQualifiedExpression callSite, @NotNull TypeEvalContext context) { + public PyType getReturnType(@NotNull Callable callable, @NotNull TypeEvalContext context) { + return null; + } + + @Override + public PyType getCallType(@NotNull PyFunction function, @Nullable PyQualifiedExpression callSite, @NotNull TypeEvalContext context) { ReturnTypeDescriptor descriptor; synchronized (myMethodToReturnTypeMap) { descriptor = myMethodToReturnTypeMap.get(function.getName()); diff --git a/python/psi-api/src/com/jetbrains/python/psi/types/TypeEvalContext.java b/python/psi-api/src/com/jetbrains/python/psi/types/TypeEvalContext.java index bc53ffdeceaf..f3c688bc88ae 100644 --- a/python/psi-api/src/com/jetbrains/python/psi/types/TypeEvalContext.java +++ b/python/psi-api/src/com/jetbrains/python/psi/types/TypeEvalContext.java @@ -18,6 +18,7 @@ package com.jetbrains.python.psi.types; import com.intellij.openapi.util.text.StringUtil; import com.intellij.psi.PsiElement; import com.intellij.psi.PsiFile; +import com.jetbrains.python.psi.Callable; import com.jetbrains.python.psi.PyTypedElement; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; @@ -41,12 +42,19 @@ public class TypeEvalContext { @Nullable private final PsiFile myOrigin; private final Map myEvaluated = new HashMap(); + private final Map myEvaluatedReturn = new HashMap(); private final ThreadLocal> myEvaluating = new ThreadLocal>() { @Override protected Set initialValue() { return new HashSet(); } }; + private final ThreadLocal> myEvaluatingReturn = new ThreadLocal>() { + @Override + protected Set initialValue() { + return new HashSet(); + } + }; private TypeEvalContext(boolean allowDataFlow, boolean allowStubToAST, @Nullable PsiFile origin) { myAllowDataFlow = allowDataFlow; @@ -114,64 +122,93 @@ public class TypeEvalContext { } return this; } - + public void trace(String message, Object... args) { if (myTrace != null) { myTrace.add(myTraceIndent + String.format(message, args)); } } - + public void traceIndent() { if (myTrace != null) { myTraceIndent += " "; } } - + public void traceUnindent() { if (myTrace != null && myTraceIndent.length() >= 2) { myTraceIndent = myTraceIndent.substring(0, myTraceIndent.length()-2); } } - + public String printTrace() { return StringUtil.join(myTrace, "\n"); } - + public boolean tracing() { return myTrace != null; } @Nullable - public PyType getType(@NotNull PyTypedElement element) { - synchronized (myEvaluated) { - if (myEvaluated.containsKey(element)) { - final PyType pyType = myEvaluated.get(element); - if (pyType != null) { - pyType.assertValid(element.toString()); - } - return pyType; - } - } + public PyType getType(@NotNull final PyTypedElement element) { final Set evaluating = myEvaluating.get(); if (evaluating.contains(element)) { return null; } evaluating.add(element); try { - PyType result = element.getType(this, Key.INSTANCE); - if (result != null) { - result.assertValid(element.toString()); - } synchronized (myEvaluated) { - myEvaluated.put(element, result); + if (myEvaluated.containsKey(element)) { + final PyType type = myEvaluated.get(element); + assertValid(type, element); + return type; + } } - return result; + final PyType type = element.getType(this, Key.INSTANCE); + assertValid(type, element); + synchronized (myEvaluated) { + myEvaluated.put(element, type); + } + return type; } finally { evaluating.remove(element); } } + @Nullable + public PyType getReturnType(@NotNull final Callable callable) { + final Set evaluating = myEvaluatingReturn.get(); + if (evaluating.contains(callable)) { + return null; + } + evaluating.add(callable); + try { + synchronized (myEvaluatedReturn) { + if (myEvaluatedReturn.containsKey(callable)) { + final PyType type = myEvaluatedReturn.get(callable); + assertValid(type, callable); + return type; + } + } + final PyType type = callable.getReturnType(this, Key.INSTANCE); + assertValid(type, callable); + synchronized (myEvaluatedReturn) { + myEvaluatedReturn.put(callable, type); + } + return type; + } + finally { + evaluating.remove(callable); + } + } + + private static void assertValid(@Nullable PyType result, @NotNull PyTypedElement element) { + if (result != null) { + result.assertValid(element.toString()); + } + } + public boolean maySwitchToAST(@NotNull PsiElement element) { return myAllowStubToAST || myOrigin == element.getContainingFile(); } diff --git a/python/src/com/jetbrains/numpy/codeInsight/NumpyDocStringTypeProvider.java b/python/src/com/jetbrains/numpy/codeInsight/NumpyDocStringTypeProvider.java index 1048e53b5682..27207ab31136 100644 --- a/python/src/com/jetbrains/numpy/codeInsight/NumpyDocStringTypeProvider.java +++ b/python/src/com/jetbrains/numpy/codeInsight/NumpyDocStringTypeProvider.java @@ -67,7 +67,7 @@ public class NumpyDocStringTypeProvider extends PyTypeProviderBase { @Nullable @Override - public PyType getReturnType(@NotNull PyFunction function, @Nullable PyQualifiedExpression callSite, @NotNull TypeEvalContext context) { + public PyType getCallType(@NotNull PyFunction function, @Nullable PyQualifiedExpression callSite, @NotNull TypeEvalContext context) { if (isInsideNumPy(function)) { final NumPyDocString docString = NumPyDocString.forFunction(function, callSite); if (docString != null) { diff --git a/python/src/com/jetbrains/pyqt/PyQtTypeProvider.java b/python/src/com/jetbrains/pyqt/PyQtTypeProvider.java index d9d030c352cd..a57a4a528e0c 100644 --- a/python/src/com/jetbrains/pyqt/PyQtTypeProvider.java +++ b/python/src/com/jetbrains/pyqt/PyQtTypeProvider.java @@ -15,12 +15,11 @@ */ package com.jetbrains.pyqt; +import com.intellij.psi.util.QualifiedName; import com.jetbrains.python.PyNames; import com.jetbrains.python.psi.Callable; import com.jetbrains.python.psi.PyClass; import com.jetbrains.python.psi.PyFunction; -import com.jetbrains.python.psi.PyQualifiedExpression; -import com.intellij.psi.util.QualifiedName; import com.jetbrains.python.psi.stubs.PyClassNameIndex; import com.jetbrains.python.psi.types.PyClassTypeImpl; import com.jetbrains.python.psi.types.PyType; @@ -37,8 +36,9 @@ public class PyQtTypeProvider extends PyTypeProviderBase { private static final String ourQt4Signal = "pyqtSignal"; @Override - public PyType getReturnType(@NotNull PyFunction function, @Nullable PyQualifiedExpression callSite, @NotNull TypeEvalContext context) { - if (PyNames.INIT.equals(function.getName())) { + public PyType getReturnType(@NotNull Callable callable, @NotNull TypeEvalContext context) { + if (PyNames.INIT.equals(callable.getName()) && callable instanceof PyFunction) { + final PyFunction function = (PyFunction)callable; final PyClass containingClass = function.getContainingClass(); if (containingClass != null && ourQt4Signal.equals(containingClass.getName())) { final String classQName = containingClass.getQualifiedName(); diff --git a/python/src/com/jetbrains/python/codeInsight/completion/PyDictKeyNamesCompletionContributor.java b/python/src/com/jetbrains/python/codeInsight/completion/PyDictKeyNamesCompletionContributor.java index cc7817fdf3d2..d7b1598f39a6 100644 --- a/python/src/com/jetbrains/python/codeInsight/completion/PyDictKeyNamesCompletionContributor.java +++ b/python/src/com/jetbrains/python/codeInsight/completion/PyDictKeyNamesCompletionContributor.java @@ -131,7 +131,7 @@ public class PyDictKeyNamesCompletionContributor extends CompletionContributor { if ("dict".equals(name)) { final TypeEvalContext context = TypeEvalContext.userInitiated(callee.getContainingFile()); final PyType type = context.getType(dictConstructor); - if (type != null && type.isBuiltin(context)) { + if (type != null && type.isBuiltin()) { final PyArgumentList list = dictConstructor.getArgumentList(); if (list == null) return; final PyExpression[] argumentList = list.getArguments(); diff --git a/python/src/com/jetbrains/python/codeInsight/intentions/PyDictConstructorToLiteralFormIntention.java b/python/src/com/jetbrains/python/codeInsight/intentions/PyDictConstructorToLiteralFormIntention.java index 322a31b6419c..8a72aa233dc5 100644 --- a/python/src/com/jetbrains/python/codeInsight/intentions/PyDictConstructorToLiteralFormIntention.java +++ b/python/src/com/jetbrains/python/codeInsight/intentions/PyDictConstructorToLiteralFormIntention.java @@ -58,7 +58,7 @@ public class PyDictConstructorToLiteralFormIntention extends BaseIntentionAction if (expression != null && expression.isCalleeText("dict")) { final TypeEvalContext context = TypeEvalContext.codeAnalysis(file); PyType type = context.getType(expression); - if (type != null && type.isBuiltin(context)) { + if (type != null && type.isBuiltin()) { PyExpression[] argumentList = expression.getArguments(); for (PyExpression argument : argumentList) { if (!(argument instanceof PyKeywordArgument)) return false; diff --git a/python/src/com/jetbrains/python/codeInsight/override/PyOverrideImplementUtil.java b/python/src/com/jetbrains/python/codeInsight/override/PyOverrideImplementUtil.java index 8783a7a16b58..39222b0fef9b 100644 --- a/python/src/com/jetbrains/python/codeInsight/override/PyOverrideImplementUtil.java +++ b/python/src/com/jetbrains/python/codeInsight/override/PyOverrideImplementUtil.java @@ -225,7 +225,7 @@ public class PyOverrideImplementUtil { statementBody.append(PyNames.PASS); } else { - if (!PyNames.INIT.equals(baseFunction.getName()) && baseFunction.getReturnType(context, null) != PyNoneType.INSTANCE) { + if (!PyNames.INIT.equals(baseFunction.getName()) && context.getReturnType(baseFunction) != PyNoneType.INSTANCE) { statementBody.append("return "); } if (baseClass.isNewStyleClass()) { diff --git a/python/src/com/jetbrains/python/codeInsight/stdlib/PyNamedTupleType.java b/python/src/com/jetbrains/python/codeInsight/stdlib/PyNamedTupleType.java index 9ffb86d2b090..ad1d6a24bdc1 100644 --- a/python/src/com/jetbrains/python/codeInsight/stdlib/PyNamedTupleType.java +++ b/python/src/com/jetbrains/python/codeInsight/stdlib/PyNamedTupleType.java @@ -89,13 +89,13 @@ public class PyNamedTupleType extends PyClassTypeImpl implements PyCallableType } @Override - public boolean isBuiltin(TypeEvalContext context) { + public boolean isBuiltin() { return false; } @Nullable @Override - public PyType getCallType(@NotNull TypeEvalContext context, @Nullable PyQualifiedExpression callSite) { + public PyType getCallType(@NotNull TypeEvalContext context, @NotNull PyQualifiedExpression callSite) { if (myDefinitionLevel > 0) { return new PyNamedTupleType(myClass, myDeclaration, myName, myFields, myDefinitionLevel-1); } diff --git a/python/src/com/jetbrains/python/codeInsight/stdlib/PyStdlibTypeProvider.java b/python/src/com/jetbrains/python/codeInsight/stdlib/PyStdlibTypeProvider.java index 9cdf4c30994a..d7678c2cd871 100644 --- a/python/src/com/jetbrains/python/codeInsight/stdlib/PyStdlibTypeProvider.java +++ b/python/src/com/jetbrains/python/codeInsight/stdlib/PyStdlibTypeProvider.java @@ -120,7 +120,7 @@ public class PyStdlibTypeProvider extends PyTypeProviderBase { @Nullable @Override - public PyType getReturnType(@NotNull PyFunction function, @Nullable PyQualifiedExpression callSite, @NotNull TypeEvalContext context) { + public PyType getCallType(@NotNull PyFunction function, @Nullable PyQualifiedExpression callSite, @NotNull TypeEvalContext context) { final String qname = getQualifiedName(function, callSite); if (qname != null) { if (OPEN_FUNCTIONS.contains(qname) && callSite != null) { diff --git a/python/src/com/jetbrains/python/codeInsight/userSkeletons/PyUserSkeletonsTypeProvider.java b/python/src/com/jetbrains/python/codeInsight/userSkeletons/PyUserSkeletonsTypeProvider.java index b900e28f93c0..6fa6529f3dfc 100644 --- a/python/src/com/jetbrains/python/codeInsight/userSkeletons/PyUserSkeletonsTypeProvider.java +++ b/python/src/com/jetbrains/python/codeInsight/userSkeletons/PyUserSkeletonsTypeProvider.java @@ -42,11 +42,12 @@ public class PyUserSkeletonsTypeProvider extends PyTypeProviderBase { return null; } + @Nullable @Override - public PyType getReturnType(@NotNull PyFunction function, @Nullable PyQualifiedExpression callSite, @NotNull TypeEvalContext context) { - final PyFunction functionSkeleton = PyUserSkeletonsUtil.getUserSkeleton(function); - if (functionSkeleton != null) { - return functionSkeleton.getReturnType(context, callSite); + public PyType getReturnType(@NotNull Callable callable, @NotNull TypeEvalContext context) { + final Callable callableSkeleton = PyUserSkeletonsUtil.getUserSkeleton(callable); + if (callableSkeleton != null) { + return context.getReturnType(callableSkeleton); } return null; } diff --git a/python/src/com/jetbrains/python/documentation/PyTypeModelBuilder.java b/python/src/com/jetbrains/python/documentation/PyTypeModelBuilder.java index ad1fa2d7f5a2..6934a37d723f 100644 --- a/python/src/com/jetbrains/python/documentation/PyTypeModelBuilder.java +++ b/python/src/com/jetbrains/python/documentation/PyTypeModelBuilder.java @@ -226,7 +226,7 @@ public class PyTypeModelBuilder { parameterModels.add(new ParamType(parameter.getName(), build(parameter.getType(myContext), true))); } } - final PyType ret = type.getCallType(myContext, null); + final PyType ret = type.getReturnType(myContext); final TypeModel returnType = build(ret, true); return new FunctionType(returnType, parameterModels); } diff --git a/python/src/com/jetbrains/python/inspections/PyBroadExceptionInspection.java b/python/src/com/jetbrains/python/inspections/PyBroadExceptionInspection.java index 2731f8d8fb11..4b0945d6742b 100644 --- a/python/src/com/jetbrains/python/inspections/PyBroadExceptionInspection.java +++ b/python/src/com/jetbrains/python/inspections/PyBroadExceptionInspection.java @@ -54,7 +54,7 @@ public class PyBroadExceptionInspection extends PyInspection { public static boolean equalsException(@NotNull PyClass cls, @NotNull TypeEvalContext context) { final PyType type = context.getType(cls); - return ("Exception".equals(cls.getName()) || "BaseException".equals(cls.getName())) && type != null && type.isBuiltin(context); + return ("Exception".equals(cls.getName()) || "BaseException".equals(cls.getName())) && type != null && type.isBuiltin(); } private static class Visitor extends PyInspectionVisitor { diff --git a/python/src/com/jetbrains/python/inspections/PyMethodMayBeStaticInspection.java b/python/src/com/jetbrains/python/inspections/PyMethodMayBeStaticInspection.java index 71f2772dae08..acf33c542e24 100644 --- a/python/src/com/jetbrains/python/inspections/PyMethodMayBeStaticInspection.java +++ b/python/src/com/jetbrains/python/inspections/PyMethodMayBeStaticInspection.java @@ -32,8 +32,6 @@ import org.jetbrains.annotations.Nls; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; -import java.util.Collection; - /** * User: ktisha * @@ -66,10 +64,10 @@ public class PyMethodMayBeStaticInspection extends PyInspection { final PyClass containingClass = node.getContainingClass(); if (containingClass == null) return; if (PythonUnitTestUtil.isUnitTestCaseClass(containingClass)) return; - final Collection supers = PySuperMethodsSearch.search(node).findAll(); - if (!supers.isEmpty()) return; - final Collection overrides = PyOverridingMethodsSearch.search(node, true).findAll(); - if (!overrides.isEmpty()) return; + final PsiElement firstSuper = PySuperMethodsSearch.search(node).findFirst(); + if (firstSuper != null) return; + final PyFunction firstOverride = PyOverridingMethodsSearch.search(node, true).findFirst(); + if (firstOverride != null) return; final PyDecoratorList decoratorList = node.getDecoratorList(); if (decoratorList != null) return; if (node.getModifier() != null) return; diff --git a/python/src/com/jetbrains/python/inspections/PyPropertyDefinitionInspection.java b/python/src/com/jetbrains/python/inspections/PyPropertyDefinitionInspection.java index 967e6a47c26d..27fff5602374 100644 --- a/python/src/com/jetbrains/python/inspections/PyPropertyDefinitionInspection.java +++ b/python/src/com/jetbrains/python/inspections/PyPropertyDefinitionInspection.java @@ -301,7 +301,9 @@ public class PyPropertyDefinitionInspection extends PyInspection { } else { PyReferenceExpression callSite = being_checked instanceof PyReferenceExpression ? (PyReferenceExpression) being_checked : null; - hasReturns = !(callable.getReturnType(myTypeEvalContext, callSite) instanceof PyNoneType); + final PyType type = callSite != null ? callable.getCallType(myTypeEvalContext, callSite) + : myTypeEvalContext.getReturnType(callable); + hasReturns = !(type instanceof PyNoneType); } if (allowed ^ hasReturns) { if (allowed && callable instanceof PyFunction) { diff --git a/python/src/com/jetbrains/python/inspections/PyProtectedMemberInspection.java b/python/src/com/jetbrains/python/inspections/PyProtectedMemberInspection.java index fe9ac7f9159e..52775764074e 100644 --- a/python/src/com/jetbrains/python/inspections/PyProtectedMemberInspection.java +++ b/python/src/com/jetbrains/python/inspections/PyProtectedMemberInspection.java @@ -70,7 +70,10 @@ public class PyProtectedMemberInspection extends PyInspection { if (name != null && name.startsWith("_") && !name.startsWith("__") && !name.endsWith("__")) { final PyClass parentClass = getClassOwner(node); if (parentClass != null) { - final PsiReference reference = node.getReference(); + final PsiReference reference = node.getReference(resolveWithoutImplicits()); + if (reference == null) { + return; + } final PsiElement resolvedExpression = reference.resolve(); final PyClass resolvedClass = getClassOwner(resolvedExpression); if (parentClass.isSubclass(resolvedClass)) diff --git a/python/src/com/jetbrains/python/inspections/PyStatementEffectInspection.java b/python/src/com/jetbrains/python/inspections/PyStatementEffectInspection.java index 28ccafd5aa12..a72ec9ef8504 100644 --- a/python/src/com/jetbrains/python/inspections/PyStatementEffectInspection.java +++ b/python/src/com/jetbrains/python/inspections/PyStatementEffectInspection.java @@ -111,7 +111,7 @@ public class PyStatementEffectInspection extends PyInspection { // maybe the op is overridden and may produce side effects, like cout << "hello" PyType type = myTypeEvalContext.getType(leftExpression); if (type != null && - !type.isBuiltin(myTypeEvalContext) && + !type.isBuiltin() && type.resolveMember(method, null, AccessDirection.READ, resolveWithoutImplicits()) != null) { return true; } @@ -119,7 +119,7 @@ public class PyStatementEffectInspection extends PyInspection { type = myTypeEvalContext.getType(rightExpression); if (type != null) { String rmethod = "__r" + method.substring(2); // __add__ -> __radd__ - if (!type.isBuiltin(myTypeEvalContext) && type.resolveMember(rmethod, null, AccessDirection.READ, resolveWithoutImplicits()) != null) { + if (!type.isBuiltin() && type.resolveMember(rmethod, null, AccessDirection.READ, resolveWithoutImplicits()) != null) { return true; } } diff --git a/python/src/com/jetbrains/python/inspections/PyStringFormatInspection.java b/python/src/com/jetbrains/python/inspections/PyStringFormatInspection.java index 180fd1bfc3b0..280822ba7175 100644 --- a/python/src/com/jetbrains/python/inspections/PyStringFormatInspection.java +++ b/python/src/com/jetbrains/python/inspections/PyStringFormatInspection.java @@ -149,7 +149,7 @@ public class PyStringFormatInspection extends PyInspection { } else if (rightExpression instanceof PyCallExpression) { final Callable callable = ((PyCallExpression)rightExpression).resolveCalleeFunction(resolveContext); - // TODO: Switch to Callable.getReturnType() + // TODO: Switch to Callable.getCallType() if (callable instanceof PyFunction && myTypeEvalContext.maySwitchToAST((PyFunction) callable)) { PyStatementList statementList = ((PyFunction)callable).getStatementList(); if (statementList == null) { diff --git a/python/src/com/jetbrains/python/inspections/PyTypeCheckerInspection.java b/python/src/com/jetbrains/python/inspections/PyTypeCheckerInspection.java index 45df382d3f83..d31d19ef4da9 100644 --- a/python/src/com/jetbrains/python/inspections/PyTypeCheckerInspection.java +++ b/python/src/com/jetbrains/python/inspections/PyTypeCheckerInspection.java @@ -102,7 +102,7 @@ public class PyTypeCheckerInspection extends PyInspection { } final PyType argType = myTypeEvalContext.getType(entry.getKey()); if (!genericsCollected) { - substitutions.putAll(PyTypeChecker.collectCallGenerics(results.getCallable(), results.getReceiver(), myTypeEvalContext)); + substitutions.putAll(PyTypeChecker.unifyReceiver(results.getReceiver(), myTypeEvalContext)); genericsCollected = true; } checkTypes(paramType, argType, entry.getKey(), myTypeEvalContext, substitutions); diff --git a/python/src/com/jetbrains/python/psi/impl/CallArgumentsMappingImpl.java b/python/src/com/jetbrains/python/psi/impl/CallArgumentsMappingImpl.java index 692ff0052b10..20e8c5dbe1dc 100644 --- a/python/src/com/jetbrains/python/psi/impl/CallArgumentsMappingImpl.java +++ b/python/src/com/jetbrains/python/psi/impl/CallArgumentsMappingImpl.java @@ -134,7 +134,7 @@ public class CallArgumentsMappingImpl implements CallArgumentsMapping { } else { PyType arg_type = context.getType(arg); - if (arg_type != null && arg_type.isBuiltin(context) && "list".equals(arg_type.getName())) { + if (arg_type != null && arg_type.isBuiltin() && "list".equals(arg_type.getName())) { mapped_args.add(arg); // we can't really analyze arbitrary lists statically yet // but ListLiteralExpressions are handled by visitor } diff --git a/python/src/com/jetbrains/python/psi/impl/PyBinaryExpressionImpl.java b/python/src/com/jetbrains/python/psi/impl/PyBinaryExpressionImpl.java index bee876ac948d..61513d1e4c4f 100644 --- a/python/src/com/jetbrains/python/psi/impl/PyBinaryExpressionImpl.java +++ b/python/src/com/jetbrains/python/psi/impl/PyBinaryExpressionImpl.java @@ -137,7 +137,7 @@ public class PyBinaryExpressionImpl extends PyElementImpl implements PyBinaryExp } final PyTypeChecker.AnalyzeCallResults results = PyTypeChecker.analyzeCall(this, context); if (results != null) { - final PyType type = results.getCallable().getReturnType(context, this); + final PyType type = results.getCallable().getCallType(context, this); if (!PyTypeChecker.isUnknown(type) && !(type instanceof PyNoneType)) { return type; } diff --git a/python/src/com/jetbrains/python/psi/impl/PyCallExpressionHelper.java b/python/src/com/jetbrains/python/psi/impl/PyCallExpressionHelper.java index c32ec1ba1566..45f094764533 100644 --- a/python/src/com/jetbrains/python/psi/impl/PyCallExpressionHelper.java +++ b/python/src/com/jetbrains/python/psi/impl/PyCallExpressionHelper.java @@ -198,7 +198,7 @@ public class PyCallExpressionHelper { final PyFunction function = (PyFunction)resolved; final Property property = function.getProperty(); if (property != null && isQualifiedByInstance(function, qualifiers, context)) { - final PyType type = function.getReturnType(context, null); + final PyType type = context.getReturnType(function); if (type instanceof PyFunctionType) { resolved = ((PyFunctionType)type).getCallable(); } @@ -424,7 +424,7 @@ public class PyCallExpressionHelper { } } if (init != null) { - final PyType t = init.getReturnType(context, (PyReferenceExpression)callee); + final PyType t = init.getCallType(context, (PyReferenceExpression)callee); if (cls != null) { if (init.getContainingClass() != cls) { if (t instanceof PyCollectionType) { @@ -453,7 +453,7 @@ public class PyCallExpressionHelper { } if (target instanceof Callable) { final Callable callable = (Callable)target; - return callable.getReturnType(context, (PyReferenceExpression)callee); + return callable.getCallType(context, (PyReferenceExpression)callee); } } } @@ -463,8 +463,14 @@ public class PyCallExpressionHelper { else { final PyType type = context.getType(callee); if (type instanceof PyCallableType) { + final PyCallableType callableType = (PyCallableType)type; final PyQualifiedExpression callSite = callee instanceof PyQualifiedExpression ? (PyQualifiedExpression)callee : null; - return ((PyCallableType) type).getCallType(context, callSite); + if (callSite != null) { + return callableType.getCallType(context, callSite); + } + else { + return callableType.getReturnType(context); + } } return null; } diff --git a/python/src/com/jetbrains/python/psi/impl/PyClassImpl.java b/python/src/com/jetbrains/python/psi/impl/PyClassImpl.java index 8e934af1f3bf..eaa480be1ac7 100644 --- a/python/src/com/jetbrains/python/psi/impl/PyClassImpl.java +++ b/python/src/com/jetbrains/python/psi/impl/PyClassImpl.java @@ -759,7 +759,7 @@ public class PyClassImpl extends PyPresentableElementImpl implement if (!(callable instanceof StubBasedPsiElement) && !context.maySwitchToAST(callable)) { return null; } - return callable.getReturnType(context, null); + return context.getReturnType(callable); } return null; } diff --git a/python/src/com/jetbrains/python/psi/impl/PyFunctionImpl.java b/python/src/com/jetbrains/python/psi/impl/PyFunctionImpl.java index 2ffc8e6cba03..acc225b67f22 100644 --- a/python/src/com/jetbrains/python/psi/impl/PyFunctionImpl.java +++ b/python/src/com/jetbrains/python/psi/impl/PyFunctionImpl.java @@ -15,7 +15,6 @@ */ package com.jetbrains.python.psi.impl; -import com.google.common.collect.Maps; import com.intellij.lang.ASTNode; import com.intellij.openapi.extensions.Extensions; import com.intellij.openapi.util.Pair; @@ -176,42 +175,74 @@ public class PyFunctionImpl extends PyPresentableElementImpl imp @Nullable @Override - public PyType getReturnType(@NotNull TypeEvalContext context, @Nullable PyQualifiedExpression callSite) { - PyType type = getGenericReturnType(context, callSite); - if (callSite == null) { - return type; + public PyType getReturnType(@NotNull TypeEvalContext context, @NotNull TypeEvalContext.Key key) { + for (PyTypeProvider typeProvider : Extensions.getExtensions(PyTypeProvider.EP_NAME)) { + final PyType returnType = typeProvider.getReturnType(this, context); + if (returnType != null) { + returnType.assertValid(typeProvider.toString()); + return returnType; + } + } + if (context.maySwitchToAST(this) && LanguageLevel.forElement(this).isAtLeast(LanguageLevel.PYTHON30)) { + PyAnnotation anno = getAnnotation(); + if (anno != null) { + PyClass pyClass = anno.resolveToClass(); + if (pyClass != null) { + return new PyClassTypeImpl(pyClass, false); + } + } + } + final PyType docStringType = getReturnTypeFromDocString(); + if (docStringType != null) { + docStringType.assertValid("from docstring"); + return docStringType; + } + if (context.allowReturnTypes(this)) { + final Ref yieldTypeRef = getYieldStatementType(context); + if (yieldTypeRef != null) { + return yieldTypeRef.get(); + } + return getReturnStatementType(context); + } + return null; + } + + @Nullable + @Override + public PyType getCallType(@NotNull TypeEvalContext context, @NotNull PyQualifiedExpression callSite) { + PyType type = null; + for (PyTypeProvider typeProvider : Extensions.getExtensions(PyTypeProvider.EP_NAME)) { + type = typeProvider.getCallType(this, callSite, context); + if (type != null) { + type.assertValid(typeProvider.toString()); + break; + } + } + if (type == null) { + type = context.getReturnType(this); } final PyTypeChecker.AnalyzeCallResults results = PyTypeChecker.analyzeCallSite(callSite, context); - if (PyTypeChecker.hasGenerics(type, context)) { - if (results != null) { - final Map substitutions = PyTypeChecker.unifyGenericCall(this, results.getReceiver(), results.getArguments(), - context); - type = substitutions != null ? PyTypeChecker.substitute(type, substitutions, context) : null; - } - else { - type = null; - } - } if (results != null) { - type = replaceSelf(type, results.getReceiver(), context); - } - if (results != null && isDynamicallyEvaluated(results.getArguments().values(), context)) { - return PyUnionType.createWeakType(type); + return analyzeCallType(type, results.getReceiver(), results.getArguments(), context); } return type; } @Nullable - /** - * Suits when there is no call site(e.g. implicit __iter__ call in statement for) - */ - public PyType getReturnTypeWithoutCallSite(@NotNull TypeEvalContext context, - @Nullable PyExpression receiver) { - PyType type = getGenericReturnType(context, null); + @Override + public PyType getCallType(@Nullable PyExpression receiver, + @NotNull Map parameters, + @NotNull TypeEvalContext context) { + return analyzeCallType(context.getReturnType(this), receiver, parameters, context); + } + + @Nullable + private PyType analyzeCallType(@Nullable PyType type, + @Nullable PyExpression receiver, + @NotNull Map parameters, + @NotNull TypeEvalContext context) { if (PyTypeChecker.hasGenerics(type, context)) { - final Map substitutions = PyTypeChecker.unifyGenericCall(this, receiver, - Maps.newHashMap(), - context); + final Map substitutions = PyTypeChecker.unifyGenericCall(receiver, parameters, context); if (substitutions != null) { type = PyTypeChecker.substitute(type, substitutions, context); } @@ -219,7 +250,13 @@ public class PyFunctionImpl extends PyPresentableElementImpl imp type = null; } } - return replaceSelf(type, receiver, context); + if (receiver != null) { + type = replaceSelf(type, receiver, context); + } + if (type != null && isDynamicallyEvaluated(parameters.values(), context)) { + type = PyUnionType.createWeakType(type); + } + return type; } @Nullable @@ -249,39 +286,6 @@ public class PyFunctionImpl extends PyPresentableElementImpl imp return false; } - @Nullable - private PyType getGenericReturnType(@NotNull TypeEvalContext typeEvalContext, @Nullable PyQualifiedExpression callSite) { - if (typeEvalContext.maySwitchToAST(this) && LanguageLevel.forElement(this).isAtLeast(LanguageLevel.PYTHON30)) { - PyAnnotation anno = getAnnotation(); - if (anno != null) { - PyClass pyClass = anno.resolveToClass(); - if (pyClass != null) { - return new PyClassTypeImpl(pyClass, false); - } - } - } - for (PyTypeProvider typeProvider : Extensions.getExtensions(PyTypeProvider.EP_NAME)) { - final PyType returnType = typeProvider.getReturnType(this, callSite, typeEvalContext); - if (returnType != null) { - returnType.assertValid(typeProvider.toString()); - return returnType; - } - } - final PyType docStringType = getReturnTypeFromDocString(); - if (docStringType != null) { - docStringType.assertValid("from docstring"); - return docStringType; - } - if (typeEvalContext.allowReturnTypes(this)) { - final Ref yieldTypeRef = getYieldStatementType(typeEvalContext); - if (yieldTypeRef != null) { - return yieldTypeRef.get(); - } - return getReturnStatementType(typeEvalContext); - } - return null; - } - @Nullable private Ref getYieldStatementType(@NotNull final TypeEvalContext context) { Ref elementType = null; @@ -386,8 +390,9 @@ public class PyFunctionImpl extends PyPresentableElementImpl imp return type; } } + final boolean hasCustomDecorators = PyUtil.hasCustomDecorators(this) && !PyUtil.isDecoratedAsAbstract(this) && getProperty() == null; final PyFunctionType type = new PyFunctionType(this); - if (getDecoratorList() != null) { + if (hasCustomDecorators) { return PyUnionType.createWeakType(type); } return type; diff --git a/python/src/com/jetbrains/python/psi/impl/PyLambdaExpressionImpl.java b/python/src/com/jetbrains/python/psi/impl/PyLambdaExpressionImpl.java index 56249f1fdfa4..0dd75915de12 100644 --- a/python/src/com/jetbrains/python/psi/impl/PyLambdaExpressionImpl.java +++ b/python/src/com/jetbrains/python/psi/impl/PyLambdaExpressionImpl.java @@ -27,6 +27,8 @@ import com.jetbrains.python.psi.types.TypeEvalContext; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; +import java.util.Map; + /** * @author yole */ @@ -61,10 +63,23 @@ public class PyLambdaExpressionImpl extends PyElementImpl implements PyLambdaExp @Nullable @Override - public PyType getReturnType(@NotNull TypeEvalContext context, @Nullable PyQualifiedExpression callSite) { + public PyType getReturnType(@NotNull TypeEvalContext context, @NotNull TypeEvalContext.Key key) { final PyExpression body = getBody(); - if (body != null) return context.getType(body); - else return null; + return body != null ? context.getType(body) : null; + } + + @Nullable + @Override + public PyType getCallType(@NotNull TypeEvalContext context, @NotNull PyQualifiedExpression callSite) { + return context.getReturnType(this); + } + + @Nullable + @Override + public PyType getCallType(@Nullable PyExpression receiver, + @NotNull Map parameters, + @NotNull TypeEvalContext context) { + return context.getReturnType(this); } @Nullable diff --git a/python/src/com/jetbrains/python/psi/impl/PyNamedParameterImpl.java b/python/src/com/jetbrains/python/psi/impl/PyNamedParameterImpl.java index 6ad33d60569e..f38937daa87f 100644 --- a/python/src/com/jetbrains/python/psi/impl/PyNamedParameterImpl.java +++ b/python/src/com/jetbrains/python/psi/impl/PyNamedParameterImpl.java @@ -208,7 +208,7 @@ public class PyNamedParameterImpl extends PyPresentableElementImplemptyMap(), context); if (enterType != null) { return enterType; } @@ -438,10 +439,7 @@ public class PyTargetExpressionImpl extends PyPresentableElementImplemptyMap(), context); } @Nullable diff --git a/python/src/com/jetbrains/python/psi/types/PyCallableTypeImpl.java b/python/src/com/jetbrains/python/psi/types/PyCallableTypeImpl.java index f417f5aa4c93..dc63784e19aa 100644 --- a/python/src/com/jetbrains/python/psi/types/PyCallableTypeImpl.java +++ b/python/src/com/jetbrains/python/psi/types/PyCallableTypeImpl.java @@ -49,7 +49,13 @@ public class PyCallableTypeImpl implements PyCallableType { @Nullable @Override - public PyType getCallType(@NotNull TypeEvalContext context, @Nullable PyQualifiedExpression callSite) { + public PyType getReturnType(@NotNull TypeEvalContext context) { + return myReturnType; + } + + @Nullable + @Override + public PyType getCallType(@NotNull TypeEvalContext context, @NotNull PyQualifiedExpression callSite) { return myReturnType; } @@ -105,7 +111,7 @@ public class PyCallableTypeImpl implements PyCallableType { } @Override - public boolean isBuiltin(TypeEvalContext context) { + public boolean isBuiltin() { return false; } diff --git a/python/src/com/jetbrains/python/psi/types/PyClassTypeImpl.java b/python/src/com/jetbrains/python/psi/types/PyClassTypeImpl.java index a63fd46bf3a7..778b8387f85e 100644 --- a/python/src/com/jetbrains/python/psi/types/PyClassTypeImpl.java +++ b/python/src/com/jetbrains/python/psi/types/PyClassTypeImpl.java @@ -163,7 +163,7 @@ public class PyClassTypeImpl extends UserDataHolderBase implements PyClassType { } } - if ("super".equals(getClassQName()) && isBuiltin(context) && location instanceof PyCallExpression) { + if ("super".equals(getClassQName()) && isBuiltin() && 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 @@ -307,13 +307,19 @@ public class PyClassTypeImpl extends UserDataHolderBase implements PyClassType { @Nullable @Override - public PyType getCallType(@NotNull TypeEvalContext context, @Nullable PyQualifiedExpression callSite) { + public PyType getReturnType(@NotNull TypeEvalContext context) { if (isDefinition()) { return new PyClassTypeImpl(getPyClass(), false); } return null; } + @Nullable + @Override + public PyType getCallType(@NotNull TypeEvalContext context, @NotNull PyQualifiedExpression callSite) { + return getReturnType(context); + } + @Nullable @Override public List getParameters(@NotNull TypeEvalContext context) { @@ -530,7 +536,7 @@ public class PyClassTypeImpl extends UserDataHolderBase implements PyClassType { } @Override - public boolean isBuiltin(TypeEvalContext context) { + public boolean isBuiltin() { return PyBuiltinCache.getInstance(myClass).isBuiltin(myClass); } diff --git a/python/src/com/jetbrains/python/psi/types/PyFunctionType.java b/python/src/com/jetbrains/python/psi/types/PyFunctionType.java index 5e0c35699067..229fe2677d12 100644 --- a/python/src/com/jetbrains/python/psi/types/PyFunctionType.java +++ b/python/src/com/jetbrains/python/psi/types/PyFunctionType.java @@ -46,8 +46,14 @@ public class PyFunctionType implements PyCallableType { @Nullable @Override - public PyType getCallType(@NotNull TypeEvalContext context, @Nullable PyQualifiedExpression callSite) { - return myCallable.getReturnType(context, callSite); + public PyType getReturnType(@NotNull TypeEvalContext context) { + return context.getReturnType(myCallable); + } + + @Nullable + @Override + public PyType getCallType(@NotNull TypeEvalContext context, @NotNull PyQualifiedExpression callSite) { + return myCallable.getCallType(context, callSite); } @Nullable @@ -79,7 +85,7 @@ public class PyFunctionType implements PyCallableType { } @Override - public boolean isBuiltin(TypeEvalContext context) { + public boolean isBuiltin() { return false; } diff --git a/python/src/com/jetbrains/python/psi/types/PyGenericType.java b/python/src/com/jetbrains/python/psi/types/PyGenericType.java index af7aaae80c44..68256ae6c3f3 100644 --- a/python/src/com/jetbrains/python/psi/types/PyGenericType.java +++ b/python/src/com/jetbrains/python/psi/types/PyGenericType.java @@ -59,7 +59,7 @@ public class PyGenericType implements PyType { } @Override - public boolean isBuiltin(TypeEvalContext context) { + public boolean isBuiltin() { return false; } diff --git a/python/src/com/jetbrains/python/psi/types/PyImportedModuleType.java b/python/src/com/jetbrains/python/psi/types/PyImportedModuleType.java index 3b56b6320289..5f66c818388d 100644 --- a/python/src/com/jetbrains/python/psi/types/PyImportedModuleType.java +++ b/python/src/com/jetbrains/python/psi/types/PyImportedModuleType.java @@ -90,7 +90,7 @@ public class PyImportedModuleType implements PyType { } @Override - public boolean isBuiltin(TypeEvalContext context) { + public boolean isBuiltin() { return false; // no module can be imported from builtins } diff --git a/python/src/com/jetbrains/python/psi/types/PyModuleType.java b/python/src/com/jetbrains/python/psi/types/PyModuleType.java index 5e495cdd4a88..f58f8f879c3b 100644 --- a/python/src/com/jetbrains/python/psi/types/PyModuleType.java +++ b/python/src/com/jetbrains/python/psi/types/PyModuleType.java @@ -407,7 +407,7 @@ public class PyModuleType implements PyType { // Modules don't descend from obje } @Override - public boolean isBuiltin(TypeEvalContext context) { + public boolean isBuiltin() { return true; } diff --git a/python/src/com/jetbrains/python/psi/types/PyNoneType.java b/python/src/com/jetbrains/python/psi/types/PyNoneType.java index 05a793182e57..417f0ce1a2f1 100644 --- a/python/src/com/jetbrains/python/psi/types/PyNoneType.java +++ b/python/src/com/jetbrains/python/psi/types/PyNoneType.java @@ -53,7 +53,7 @@ public class PyNoneType implements PyType { // TODO must extend ClassType. It's } @Override - public boolean isBuiltin(TypeEvalContext context) { + public boolean isBuiltin() { return true; } diff --git a/python/src/com/jetbrains/python/psi/types/PyTupleType.java b/python/src/com/jetbrains/python/psi/types/PyTupleType.java index 8ede14ea8c99..ae6ca00b3dbd 100644 --- a/python/src/com/jetbrains/python/psi/types/PyTupleType.java +++ b/python/src/com/jetbrains/python/psi/types/PyTupleType.java @@ -63,7 +63,7 @@ public class PyTupleType extends PyClassTypeImpl implements PySubscriptableType } @Override - public boolean isBuiltin(TypeEvalContext context) { + public boolean isBuiltin() { return true; } diff --git a/python/src/com/jetbrains/python/psi/types/PyTypeChecker.java b/python/src/com/jetbrains/python/psi/types/PyTypeChecker.java index 632633bc6501..cbdeb163e923 100644 --- a/python/src/com/jetbrains/python/psi/types/PyTypeChecker.java +++ b/python/src/com/jetbrains/python/psi/types/PyTypeChecker.java @@ -20,7 +20,6 @@ import com.intellij.psi.PsiPolyVariantReference; import com.intellij.psi.PsiReference; import com.intellij.psi.ResolveResult; import com.jetbrains.python.PyNames; -import com.jetbrains.python.codeInsight.stdlib.PyStdlibTypeProvider; import com.jetbrains.python.psi.*; import com.jetbrains.python.psi.resolve.PyResolveContext; import com.jetbrains.python.psi.resolve.RatedResolveResult; @@ -172,8 +171,7 @@ public class PyTypeChecker { } } } - if (!match(expectedCallable.getCallType(context, null), actualCallable.getCallType(context, null), context, substitutions, - recursive)) { + if (!match(expectedCallable.getReturnType(context), actualCallable.getReturnType(context), context, substitutions, recursive)) { return false; } return true; @@ -259,7 +257,7 @@ public class PyTypeChecker { } } } - collectGenerics(callable.getCallType(context, null), context, collected, visited); + collectGenerics(callable.getReturnType(context), context, collected, visited); } } @@ -309,7 +307,7 @@ public class PyTypeChecker { substParams.add(subst); } } - final PyType substResult = substitute(callable.getCallType(context, null), substitutions, context); + final PyType substResult = substitute(callable.getReturnType(context), substitutions, context); return new PyCallableTypeImpl(substParams, substResult); } } @@ -317,11 +315,10 @@ public class PyTypeChecker { } @Nullable - public static Map unifyGenericCall(@NotNull PyFunction function, - @Nullable PyExpression receiver, + public static Map unifyGenericCall(@Nullable PyExpression receiver, @NotNull Map arguments, @NotNull TypeEvalContext context) { - final Map substitutions = collectCallGenerics(function, receiver, context); + final Map substitutions = unifyReceiver(receiver, context); for (Map.Entry entry : arguments.entrySet()) { final PyNamedParameter p = entry.getValue(); if (p.isPositionalContainer() || p.isKeywordContainer()) { @@ -337,8 +334,7 @@ public class PyTypeChecker { } @NotNull - public static Map collectCallGenerics(@NotNull Callable callable, @Nullable PyExpression receiver, - @NotNull TypeEvalContext context) { + public static Map unifyReceiver(@Nullable PyExpression receiver, @NotNull TypeEvalContext context) { final Map substitutions = new LinkedHashMap(); // Collect generic params of object type final Set generics = new LinkedHashSet(); @@ -347,14 +343,22 @@ public class PyTypeChecker { for (PyGenericType t : generics) { substitutions.put(t, t); } - final PyClass cls = (callable instanceof PyFunction) ? ((PyFunction)callable).getContainingClass() : null; - if (cls != null) { - final PyFunction init = cls.findInitOrNew(true); - // Unify generics in constructor - if (init != null) { - final PyType initType = init.getReturnType(context, null); - if (initType != null) { - match(initType, qualifierType, context, substitutions); + // Unify generics in constructor + if (qualifierType != null) { + final PyResolveContext resolveContext = PyResolveContext.noImplicits().withTypeEvalContext(context); + // TODO: Resolve to __new__ as well + final List results = qualifierType.resolveMember(PyNames.INIT, null, AccessDirection.READ, + resolveContext); + if (results != null && !results.isEmpty()) { + final PsiElement init = results.get(0).getElement(); + if (init instanceof PyTypedElement) { + final PyType initType = context.getType((PyTypedElement)init); + if (initType instanceof PyCallableType) { + final PyType initReturnType = ((PyCallableType)initType).getReturnType(context); + if (initReturnType != null) { + match(initReturnType, qualifierType, context, substitutions); + } + } } } } diff --git a/python/src/com/jetbrains/python/psi/types/PyUnionType.java b/python/src/com/jetbrains/python/psi/types/PyUnionType.java index abdc5ad34428..8572b72c1771 100644 --- a/python/src/com/jetbrains/python/psi/types/PyUnionType.java +++ b/python/src/com/jetbrains/python/psi/types/PyUnionType.java @@ -77,13 +77,12 @@ public class PyUnionType implements PyType { } /** - * @param context * @return true if all types in the union are built-in. */ @Override - public boolean isBuiltin(TypeEvalContext context) { + public boolean isBuiltin() { for (PyType one : myMembers) { - if (one == null || !one.isBuiltin(context)) return false; + if (one == null || !one.isBuiltin()) return false; } return true; } diff --git a/python/src/com/jetbrains/python/refactoring/introduce/IntroduceHandler.java b/python/src/com/jetbrains/python/refactoring/introduce/IntroduceHandler.java index 7f774010aa8f..f479d104765e 100644 --- a/python/src/com/jetbrains/python/refactoring/introduce/IntroduceHandler.java +++ b/python/src/com/jetbrains/python/refactoring/introduce/IntroduceHandler.java @@ -207,7 +207,7 @@ abstract public class IntroduceHandler implements RefactoringActionHandler { if (type != null && type != PyNoneType.INSTANCE) { String typeName = type.getName(); if (typeName != null) { - if (type.isBuiltin(context)) { + if (type.isBuiltin()) { typeName = typeName.substring(0, 1); } candidates.addAll(NameSuggesterUtil.generateNamesByType(typeName)); diff --git a/python/testSrc/com/jetbrains/python/PyTypeParserTest.java b/python/testSrc/com/jetbrains/python/PyTypeParserTest.java index 30dc43404a3c..7db230a1d7c1 100644 --- a/python/testSrc/com/jetbrains/python/PyTypeParserTest.java +++ b/python/testSrc/com/jetbrains/python/PyTypeParserTest.java @@ -252,7 +252,7 @@ public class PyTypeParserTest extends PyTestCase { final PyCallableType callableType = (PyCallableType)type; assertNotNull(callableType); final TypeEvalContext context = getTypeEvalContext(); - final PyType returnType = callableType.getCallType(context, null); + final PyType returnType = callableType.getReturnType(context); assertInstanceOf(returnType, PyGenericType.class); final List parameterTypes = callableType.getParameters(context); assertNotNull(parameterTypes); @@ -271,7 +271,7 @@ public class PyTypeParserTest extends PyTestCase { assertInstanceOf(type, PyCallableType.class); final PyCallableType callableType = (PyCallableType)type; assertNotNull(callableType); - final PyType returnType = callableType.getCallType(getTypeEvalContext(), null); + final PyType returnType = callableType.getReturnType(getTypeEvalContext()); assertNotNull(returnType); assertEquals("int", returnType.getName()); final List parameterTypes = callableType.getParameters(getTypeEvalContext());