mirror of
https://gitflic.ru/project/openide/openide.git
synced 2026-09-27 10:03:11 +07:00
Cache return types of callables using TypeEvalContext.getReturnType()
This commit is contained in:
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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.
|
||||
|
||||
@@ -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.
|
||||
|
||||
@@ -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<PyTypedElement, PyType> myEvaluated = new HashMap<PyTypedElement, PyType>();
|
||||
private final Map<Callable, PyType> myEvaluatedReturn = new HashMap<Callable, PyType>();
|
||||
private final ThreadLocal<Set<PyTypedElement>> myEvaluating = new ThreadLocal<Set<PyTypedElement>>() {
|
||||
@Override
|
||||
protected Set<PyTypedElement> initialValue() {
|
||||
return new HashSet<PyTypedElement>();
|
||||
}
|
||||
};
|
||||
private final ThreadLocal<Set<Callable>> myEvaluatingReturn = new ThreadLocal<Set<Callable>>() {
|
||||
@Override
|
||||
protected Set<Callable> initialValue() {
|
||||
return new HashSet<Callable>();
|
||||
}
|
||||
};
|
||||
|
||||
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<PyTypedElement> 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<Callable> 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();
|
||||
}
|
||||
|
||||
@@ -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()) {
|
||||
|
||||
+1
-1
@@ -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;
|
||||
}
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
@@ -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) {
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -750,7 +750,7 @@ public class PyClassImpl extends PyPresentableElementImpl<PyClassStub> implement
|
||||
if (!(callable instanceof StubBasedPsiElement) && !context.maySwitchToAST(callable)) {
|
||||
return null;
|
||||
}
|
||||
return callable.getReturnType(context);
|
||||
return context.getReturnType(callable);
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
@@ -175,20 +175,7 @@ public class PyFunctionImpl extends PyPresentableElementImpl<PyFunctionStub> 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<PyFunctionStub> 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<PyFunctionStub> imp
|
||||
public PyType getCallType(@Nullable PyExpression receiver,
|
||||
@NotNull Map<PyExpression, PyNamedParameter> 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<PyFunctionStub> 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);
|
||||
}
|
||||
|
||||
@@ -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<PyExpression, PyNamedParameter> parameters,
|
||||
@NotNull TypeEvalContext context) {
|
||||
return getReturnType(context);
|
||||
return context.getReturnType(this);
|
||||
}
|
||||
|
||||
@Nullable
|
||||
|
||||
@@ -208,7 +208,7 @@ public class PyNamedParameterImpl extends PyPresentableElementImpl<PyNamedParame
|
||||
PyType initType = null;
|
||||
final PyFunction init = containingClass.findInitOrNew(true);
|
||||
if (init != null && init != func) {
|
||||
initType = init.getReturnType(context);
|
||||
initType = context.getReturnType(init);
|
||||
if (init.getContainingClass() != containingClass) {
|
||||
if (initType instanceof PyCollectionType) {
|
||||
final PyType elementType = ((PyCollectionType)initType).getElementType(context);
|
||||
|
||||
@@ -49,7 +49,7 @@ public class PyCallableTypeImpl implements PyCallableType {
|
||||
|
||||
@Nullable
|
||||
@Override
|
||||
public PyType getReturnType() {
|
||||
public PyType getReturnType(@NotNull TypeEvalContext context) {
|
||||
return myReturnType;
|
||||
}
|
||||
|
||||
|
||||
@@ -307,7 +307,7 @@ public class PyClassTypeImpl extends UserDataHolderBase implements PyClassType {
|
||||
|
||||
@Nullable
|
||||
@Override
|
||||
public PyType getReturnType() {
|
||||
public PyType getReturnType(@NotNull TypeEvalContext context) {
|
||||
if (isDefinition()) {
|
||||
return new PyClassTypeImpl(getPyClass(), false);
|
||||
}
|
||||
@@ -317,7 +317,7 @@ public class PyClassTypeImpl extends UserDataHolderBase implements PyClassType {
|
||||
@Nullable
|
||||
@Override
|
||||
public PyType getCallType(@NotNull TypeEvalContext context, @NotNull PyQualifiedExpression callSite) {
|
||||
return getReturnType();
|
||||
return getReturnType(context);
|
||||
}
|
||||
|
||||
@Nullable
|
||||
|
||||
@@ -34,11 +34,9 @@ import java.util.List;
|
||||
*/
|
||||
public class PyFunctionType implements PyCallableType {
|
||||
@NotNull private final Callable myCallable;
|
||||
@Nullable private final PyType myReturnType;
|
||||
|
||||
public PyFunctionType(@NotNull Callable callable, @Nullable PyType returnType) {
|
||||
public PyFunctionType(@NotNull Callable callable) {
|
||||
myCallable = callable;
|
||||
myReturnType = returnType;
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -48,8 +46,8 @@ public class PyFunctionType implements PyCallableType {
|
||||
|
||||
@Nullable
|
||||
@Override
|
||||
public PyType getReturnType() {
|
||||
return myReturnType;
|
||||
public PyType getReturnType(@NotNull TypeEvalContext context) {
|
||||
return context.getReturnType(myCallable);
|
||||
}
|
||||
|
||||
@Nullable
|
||||
|
||||
@@ -171,7 +171,7 @@ public class PyTypeChecker {
|
||||
}
|
||||
}
|
||||
}
|
||||
if (!match(expectedCallable.getReturnType(), actualCallable.getReturnType(), context, substitutions, recursive)) {
|
||||
if (!match(expectedCallable.getReturnType(context), actualCallable.getReturnType(context), context, substitutions, recursive)) {
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
@@ -257,7 +257,7 @@ public class PyTypeChecker {
|
||||
}
|
||||
}
|
||||
}
|
||||
collectGenerics(callable.getReturnType(), context, collected, visited);
|
||||
collectGenerics(callable.getReturnType(context), context, collected, visited);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -307,7 +307,7 @@ public class PyTypeChecker {
|
||||
substParams.add(subst);
|
||||
}
|
||||
}
|
||||
final PyType substResult = substitute(callable.getReturnType(), substitutions, context);
|
||||
final PyType substResult = substitute(callable.getReturnType(context), substitutions, context);
|
||||
return new PyCallableTypeImpl(substParams, substResult);
|
||||
}
|
||||
}
|
||||
@@ -354,7 +354,7 @@ public class PyTypeChecker {
|
||||
if (init instanceof PyTypedElement) {
|
||||
final PyType initType = context.getType((PyTypedElement)init);
|
||||
if (initType instanceof PyCallableType) {
|
||||
final PyType initReturnType = ((PyCallableType)initType).getReturnType();
|
||||
final PyType initReturnType = ((PyCallableType)initType).getReturnType(context);
|
||||
if (initReturnType != null) {
|
||||
match(initReturnType, qualifierType, context, substitutions);
|
||||
}
|
||||
|
||||
@@ -252,7 +252,7 @@ public class PyTypeParserTest extends PyTestCase {
|
||||
final PyCallableType callableType = (PyCallableType)type;
|
||||
assertNotNull(callableType);
|
||||
final TypeEvalContext context = getTypeEvalContext();
|
||||
final PyType returnType = callableType.getReturnType();
|
||||
final PyType returnType = callableType.getReturnType(context);
|
||||
assertInstanceOf(returnType, PyGenericType.class);
|
||||
final List<PyCallableParameter> 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<PyCallableParameter> parameterTypes = callableType.getParameters(getTypeEvalContext());
|
||||
|
||||
Reference in New Issue
Block a user