From 6b8002e34ad150ca0f82539eef5c61e39b000a47 Mon Sep 17 00:00:00 2001 From: Andrey Vlasovskikh Date: Mon, 3 Mar 2014 19:29:39 +0400 Subject: [PATCH] Cache return types of callables using TypeEvalContext.getReturnType() --- .../python/psi/impl/PyJavaClassType.java | 4 +- .../python/psi/impl/PyJavaMethodType.java | 4 +- .../com/jetbrains/python/psi/Callable.java | 2 +- .../python/psi/types/PyCallableType.java | 3 +- .../python/psi/types/TypeEvalContext.java | 79 ++++++++++++++----- .../override/PyOverrideImplementUtil.java | 2 +- .../PyUserSkeletonsTypeProvider.java | 2 +- .../documentation/PyTypeModelBuilder.java | 2 +- .../PyPropertyDefinitionInspection.java | 2 +- .../psi/impl/PyCallExpressionHelper.java | 4 +- .../python/psi/impl/PyClassImpl.java | 2 +- .../python/psi/impl/PyFunctionImpl.java | 22 +----- .../psi/impl/PyLambdaExpressionImpl.java | 18 +---- .../python/psi/impl/PyNamedParameterImpl.java | 2 +- .../python/psi/types/PyCallableTypeImpl.java | 2 +- .../python/psi/types/PyClassTypeImpl.java | 4 +- .../python/psi/types/PyFunctionType.java | 8 +- .../python/psi/types/PyTypeChecker.java | 8 +- .../jetbrains/python/PyTypeParserTest.java | 4 +- 19 files changed, 93 insertions(+), 81 deletions(-) diff --git a/python/pluginSrc/com/jetbrains/python/psi/impl/PyJavaClassType.java b/python/pluginSrc/com/jetbrains/python/psi/impl/PyJavaClassType.java index c2e5938776e7..a667bb69f849 100644 --- a/python/pluginSrc/com/jetbrains/python/psi/impl/PyJavaClassType.java +++ b/python/pluginSrc/com/jetbrains/python/psi/impl/PyJavaClassType.java @@ -104,7 +104,7 @@ public class PyJavaClassType implements PyClassLikeType { @Nullable @Override - public PyType getReturnType() { + public PyType getReturnType(@NotNull TypeEvalContext context) { if (myDefinition) { return new PyJavaClassType(myClass, false); } @@ -114,7 +114,7 @@ public class PyJavaClassType implements PyClassLikeType { @Nullable @Override public PyType getCallType(@NotNull TypeEvalContext context, @NotNull PyQualifiedExpression callSite) { - return getReturnType(); + return getReturnType(context); } @Nullable diff --git a/python/pluginSrc/com/jetbrains/python/psi/impl/PyJavaMethodType.java b/python/pluginSrc/com/jetbrains/python/psi/impl/PyJavaMethodType.java index 8287b0b4c6cd..6f0fd48e8742 100644 --- a/python/pluginSrc/com/jetbrains/python/psi/impl/PyJavaMethodType.java +++ b/python/pluginSrc/com/jetbrains/python/psi/impl/PyJavaMethodType.java @@ -52,14 +52,14 @@ public class PyJavaMethodType implements PyCallableType { @Nullable @Override - public PyType getReturnType() { + public PyType getReturnType(@NotNull TypeEvalContext context) { return PyJavaTypeProvider.asPyType(myMethod.getReturnType()); } @Nullable @Override public PyType getCallType(@NotNull TypeEvalContext context, @NotNull PyQualifiedExpression callSite) { - return getReturnType(); + return getReturnType(context); } @Nullable 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 2dbded6faae7..a31197ec60c1 100644 --- a/python/psi-api/src/com/jetbrains/python/psi/Callable.java +++ b/python/psi-api/src/com/jetbrains/python/psi/Callable.java @@ -39,7 +39,7 @@ public interface Callable extends PyTypedElement, PyQualifiedNameOwner { * Returns the return type of the callable independent of a call site. */ @Nullable - PyType getReturnType(@NotNull TypeEvalContext context); + PyType getReturnType(@NotNull TypeEvalContext context, @NotNull TypeEvalContext.Key key); /** * Returns the type of the call to the callable. 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 6797ca69e4a5..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 @@ -37,9 +37,10 @@ public interface PyCallableType extends PyType { * Returns the return type of a function independent of a call site. * * For example, it may return a generic type. + * @param context */ @Nullable - PyType getReturnType(); + PyType getReturnType(@NotNull TypeEvalContext context); /** * Returns the type which is the result of calling an instance of this type. 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/python/codeInsight/override/PyOverrideImplementUtil.java b/python/src/com/jetbrains/python/codeInsight/override/PyOverrideImplementUtil.java index cce526ad60ec..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) != 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/userSkeletons/PyUserSkeletonsTypeProvider.java b/python/src/com/jetbrains/python/codeInsight/userSkeletons/PyUserSkeletonsTypeProvider.java index 6dbb63ad56da..6fa6529f3dfc 100644 --- a/python/src/com/jetbrains/python/codeInsight/userSkeletons/PyUserSkeletonsTypeProvider.java +++ b/python/src/com/jetbrains/python/codeInsight/userSkeletons/PyUserSkeletonsTypeProvider.java @@ -47,7 +47,7 @@ public class PyUserSkeletonsTypeProvider extends PyTypeProviderBase { public PyType getReturnType(@NotNull Callable callable, @NotNull TypeEvalContext context) { final Callable callableSkeleton = PyUserSkeletonsUtil.getUserSkeleton(callable); if (callableSkeleton != null) { - return callableSkeleton.getReturnType(context); + 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 519e6a4afb2c..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.getReturnType(); + 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/PyPropertyDefinitionInspection.java b/python/src/com/jetbrains/python/inspections/PyPropertyDefinitionInspection.java index 945929e866d6..27fff5602374 100644 --- a/python/src/com/jetbrains/python/inspections/PyPropertyDefinitionInspection.java +++ b/python/src/com/jetbrains/python/inspections/PyPropertyDefinitionInspection.java @@ -302,7 +302,7 @@ public class PyPropertyDefinitionInspection extends PyInspection { else { PyReferenceExpression callSite = being_checked instanceof PyReferenceExpression ? (PyReferenceExpression) being_checked : null; final PyType type = callSite != null ? callable.getCallType(myTypeEvalContext, callSite) - : callable.getReturnType(myTypeEvalContext); + : myTypeEvalContext.getReturnType(callable); hasReturns = !(type instanceof PyNoneType); } if (allowed ^ hasReturns) { diff --git a/python/src/com/jetbrains/python/psi/impl/PyCallExpressionHelper.java b/python/src/com/jetbrains/python/psi/impl/PyCallExpressionHelper.java index 7eae40ba90c9..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); + final PyType type = context.getReturnType(function); if (type instanceof PyFunctionType) { resolved = ((PyFunctionType)type).getCallable(); } @@ -469,7 +469,7 @@ public class PyCallExpressionHelper { return callableType.getCallType(context, callSite); } else { - return callableType.getReturnType(); + 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 cc112040964d..b0f63eca8612 100644 --- a/python/src/com/jetbrains/python/psi/impl/PyClassImpl.java +++ b/python/src/com/jetbrains/python/psi/impl/PyClassImpl.java @@ -750,7 +750,7 @@ public class PyClassImpl extends PyPresentableElementImpl implement if (!(callable instanceof StubBasedPsiElement) && !context.maySwitchToAST(callable)) { return null; } - return callable.getReturnType(context); + 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 1c29810d6f1a..acc225b67f22 100644 --- a/python/src/com/jetbrains/python/psi/impl/PyFunctionImpl.java +++ b/python/src/com/jetbrains/python/psi/impl/PyFunctionImpl.java @@ -175,20 +175,7 @@ public class PyFunctionImpl extends PyPresentableElementImpl imp @Nullable @Override - public PyType getReturnType(@NotNull TypeEvalContext context) { - PyType type = context.getType(this); - if (type instanceof PyUnionType) { - final PyUnionType unionType = (PyUnionType)type; - type = unionType.excludeNull(); - } - if (type instanceof PyCallableType) { - return ((PyCallableType)type).getReturnType(); - } - return null; - } - - @Nullable - private PyType calculateReturnType(@NotNull TypeEvalContext context) { + 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) { @@ -232,7 +219,7 @@ public class PyFunctionImpl extends PyPresentableElementImpl imp } } if (type == null) { - type = getReturnType(context); + type = context.getReturnType(this); } final PyTypeChecker.AnalyzeCallResults results = PyTypeChecker.analyzeCallSite(callSite, context); if (results != null) { @@ -246,7 +233,7 @@ public class PyFunctionImpl extends PyPresentableElementImpl imp public PyType getCallType(@Nullable PyExpression receiver, @NotNull Map parameters, @NotNull TypeEvalContext context) { - return analyzeCallType(getReturnType(context), receiver, parameters, context); + return analyzeCallType(context.getReturnType(this), receiver, parameters, context); } @Nullable @@ -404,8 +391,7 @@ public class PyFunctionImpl extends PyPresentableElementImpl imp } } final boolean hasCustomDecorators = PyUtil.hasCustomDecorators(this) && !PyUtil.isDecoratedAsAbstract(this) && getProperty() == null; - final PyType returnType = calculateReturnType(context); - final PyFunctionType type = new PyFunctionType(this, hasCustomDecorators ? PyUnionType.createWeakType(returnType) : returnType); + final PyFunctionType type = new PyFunctionType(this); if (hasCustomDecorators) { return PyUnionType.createWeakType(type); } diff --git a/python/src/com/jetbrains/python/psi/impl/PyLambdaExpressionImpl.java b/python/src/com/jetbrains/python/psi/impl/PyLambdaExpressionImpl.java index 87d081050f12..0dd75915de12 100644 --- a/python/src/com/jetbrains/python/psi/impl/PyLambdaExpressionImpl.java +++ b/python/src/com/jetbrains/python/psi/impl/PyLambdaExpressionImpl.java @@ -21,7 +21,6 @@ import com.intellij.psi.util.PsiTreeUtil; import com.jetbrains.python.PyElementTypes; import com.jetbrains.python.codeInsight.controlflow.ControlFlowCache; import com.jetbrains.python.psi.*; -import com.jetbrains.python.psi.types.PyCallableType; import com.jetbrains.python.psi.types.PyFunctionType; import com.jetbrains.python.psi.types.PyType; import com.jetbrains.python.psi.types.TypeEvalContext; @@ -49,7 +48,7 @@ public class PyLambdaExpressionImpl extends PyElementImpl implements PyLambdaExp return type; } } - return new PyFunctionType(this, calculateReturnType(context)); + return new PyFunctionType(this); } @NotNull @@ -64,16 +63,7 @@ public class PyLambdaExpressionImpl extends PyElementImpl implements PyLambdaExp @Nullable @Override - public PyType getReturnType(@NotNull TypeEvalContext context) { - final PyType type = context.getType(this); - if (type instanceof PyCallableType) { - return ((PyCallableType)type).getReturnType(); - } - return null; - } - - @Nullable - private PyType calculateReturnType(@NotNull TypeEvalContext context) { + public PyType getReturnType(@NotNull TypeEvalContext context, @NotNull TypeEvalContext.Key key) { final PyExpression body = getBody(); return body != null ? context.getType(body) : null; } @@ -81,7 +71,7 @@ public class PyLambdaExpressionImpl extends PyElementImpl implements PyLambdaExp @Nullable @Override public PyType getCallType(@NotNull TypeEvalContext context, @NotNull PyQualifiedExpression callSite) { - return getReturnType(context); + return context.getReturnType(this); } @Nullable @@ -89,7 +79,7 @@ public class PyLambdaExpressionImpl extends PyElementImpl implements PyLambdaExp public PyType getCallType(@Nullable PyExpression receiver, @NotNull Map parameters, @NotNull TypeEvalContext context) { - return getReturnType(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 04771779a780..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 PyPresentableElementImpl 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.getReturnType(); + final PyType returnType = callableType.getReturnType(getTypeEvalContext()); assertNotNull(returnType); assertEquals("int", returnType.getName()); final List parameterTypes = callableType.getParameters(getTypeEvalContext());