PY-18791 Fixed: False positive in type information when returning callable from property

Consider property call in PyCallExpressionHelper.getCallTargetReturnType.
This commit is contained in:
Semyon Proshev
2017-08-01 12:49:21 +03:00
parent 424c9ad668
commit 8585977394
2 changed files with 40 additions and 0 deletions
@@ -584,6 +584,10 @@ public class PyCallExpressionHelper {
if (providedType instanceof PyCallableType) {
return Ref.create(((PyCallableType)providedType).getCallType(context, call));
}
final Ref<PyType> propertyCallType = getPropertyCallType(call, target, context);
if (propertyCallType != null) {
return propertyCallType;
}
if (target instanceof PyCallable) {
final PyCallable callable = (PyCallable)target;
return Ref.create(callable.getCallType(context, call));
@@ -591,6 +595,26 @@ public class PyCallExpressionHelper {
return null;
}
@Nullable
private static Ref<PyType> getPropertyCallType(@NotNull PyCallExpression call,
@NotNull PsiElement target,
@NotNull TypeEvalContext context) {
if (target instanceof PyCallable && target instanceof PyPossibleClassMember) {
final PyClass containingClass = ((PyPossibleClassMember)target).getContainingClass();
if (containingClass != null) {
final PyCallable callable = (PyCallable)target;
final Property property = containingClass.findPropertyByCallable(callable);
if (property != null) {
final PyType propertyType = property.getType(call.getReceiver(callable), context);
if (propertyType instanceof PyCallableType) {
return Ref.create(((PyCallableType)propertyType).getCallType(context, call));
}
}
}
}
return null;
}
@NotNull
private static Maybe<PyType> getSuperCallType(@NotNull PyCallExpression call, TypeEvalContext context) {
final PyExpression callee = call.getCallee();
@@ -2221,6 +2221,22 @@ public class PyTypeTest extends PyTestCase {
);
}
// PY-18791
public void testCallOnProperty() {
runWithLanguageLevel(
LanguageLevel.PYTHON35,
() -> doTest("Iterator[int]",
"from typing import Iterator, Callable\n" +
"class Foo:\n" +
" def iterate(self) -> Iterator[int]:\n" +
" pass\n" +
" @property\n" +
" def foo(self) -> Callable[[], Iterator[int]]:\n" +
" return self.iterate\n" +
"expr = Foo().foo()")
);
}
private static List<TypeEvalContext> getTypeEvalContexts(@NotNull PyExpression element) {
return ImmutableList.of(TypeEvalContext.codeAnalysis(element.getProject(), element.getContainingFile()).withTracing(),
TypeEvalContext.userInitiated(element.getProject(), element.getContainingFile()).withTracing());