Fixed analyzing calls of properties that return callables (PY-9605)

This commit is contained in:
Andrey Vlasovskikh
2013-09-27 16:33:04 +04:00
parent 539ba37e9e
commit fa9c70f41a
4 changed files with 46 additions and 5 deletions
@@ -176,6 +176,21 @@ public class PyCallExpressionHelper {
else if (PyNames.STATICMETHOD.equals(wrapper_name)) wrappedModifier = PyFunction.Modifier.STATICMETHOD;
}
}
final List<PyExpression> qualifiers = resolveResult != null ? resolveResult.getQualifiers() : Collections.<PyExpression>emptyList();
final TypeEvalContext context = resolveContext.getTypeEvalContext();
if (resolved instanceof PyFunction) {
final PyFunction function = (PyFunction)resolved;
final Property property = function.getProperty();
if (property != null && isQualifiedByInstance(function, qualifiers, context)) {
final PyType type = function.getReturnType(context, null);
if (type instanceof PyFunctionType) {
resolved = ((PyFunctionType)type).getCallable();
}
else {
resolved = null;
}
}
}
if (resolved instanceof Callable) {
PyFunction.Modifier modifier = resolved instanceof PyFunction
? ((PyFunction)resolved).getModifier()
@@ -183,12 +198,10 @@ public class PyCallExpressionHelper {
if (modifier == null && wrappedModifier != null) {
modifier = wrappedModifier;
}
List<PyExpression> qualifiers = resolveResult != null ? resolveResult.getQualifiers() : Collections.<PyExpression>emptyList();
boolean isByInstance = isConstructorCall ||
isQualifiedByInstance((Callable)resolved, qualifiers, resolveContext.getTypeEvalContext())
|| resolved instanceof PyBoundFunction;
boolean isByInstance = isConstructorCall || isQualifiedByInstance((Callable)resolved, qualifiers, context)
|| resolved instanceof PyBoundFunction;
PyExpression lastQualifier = qualifiers != null && qualifiers.isEmpty() ? null : qualifiers.get(qualifiers.size()-1);
boolean isByClass = lastQualifier == null ? false : isQualifiedByClass((Callable)resolved, lastQualifier, resolveContext.getTypeEvalContext());
boolean isByClass = lastQualifier == null ? false : isQualifiedByClass((Callable)resolved, lastQualifier, context);
final Callable callable = (Callable)resolved;
implicitOffset += getImplicitArgumentCount(callable, modifier, isConstructorCall, isByInstance, isByClass);
@@ -0,0 +1,10 @@
class C(object):
@property
def f(self):
return lambda x, y: (x, y)
c = C()
c.f(1, 2)
c.f(<warning descr="Parameter 'x' unfilled"><warning descr="Parameter 'y' unfilled">)</warning></warning>
c.f(1, 2, <warning descr="Unexpected argument">3</warning>)
@@ -771,6 +771,19 @@ public class PyTypeTest extends PyTestCase {
" expr = self\n");
}
// PY-9605
public void testPropertyReturnsCallable() {
doTest("() -> int",
"class C(object):\n" +
" @property\n" +
" def foo(self):\n" +
" return lambda: 0\n" +
"\n" +
"c = C()\n" +
"expr = c.foo\n");
}
private static TypeEvalContext getTypeEvalContext(@NotNull PyExpression element) {
return TypeEvalContext.userInitiated(element.getContainingFile()).withTracing();
}
@@ -138,4 +138,9 @@ public class PyArgumentListInspectionTest extends PyTestCase {
public void testDecoratedChangedParameters() {
doTest();
}
// PY-9605
public void testPropertyReturnsCallable() {
doTest();
}
}