From fa9c70f41acd8a6c192c7c27250bff47c91cbc37 Mon Sep 17 00:00:00 2001 From: Andrey Vlasovskikh Date: Fri, 27 Sep 2013 16:33:04 +0400 Subject: [PATCH] Fixed analyzing calls of properties that return callables (PY-9605) --- .../psi/impl/PyCallExpressionHelper.java | 23 +++++++++++++++---- .../propertyReturnsCallable.py | 10 ++++++++ .../com/jetbrains/python/PyTypeTest.java | 13 +++++++++++ .../PyArgumentListInspectionTest.java | 5 ++++ 4 files changed, 46 insertions(+), 5 deletions(-) create mode 100644 python/testData/inspections/PyArgumentListInspection/propertyReturnsCallable.py diff --git a/python/src/com/jetbrains/python/psi/impl/PyCallExpressionHelper.java b/python/src/com/jetbrains/python/psi/impl/PyCallExpressionHelper.java index 1f3d65c93fa5..8416a3627f91 100644 --- a/python/src/com/jetbrains/python/psi/impl/PyCallExpressionHelper.java +++ b/python/src/com/jetbrains/python/psi/impl/PyCallExpressionHelper.java @@ -176,6 +176,21 @@ public class PyCallExpressionHelper { else if (PyNames.STATICMETHOD.equals(wrapper_name)) wrappedModifier = PyFunction.Modifier.STATICMETHOD; } } + final List qualifiers = resolveResult != null ? resolveResult.getQualifiers() : Collections.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 qualifiers = resolveResult != null ? resolveResult.getQualifiers() : Collections.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); diff --git a/python/testData/inspections/PyArgumentListInspection/propertyReturnsCallable.py b/python/testData/inspections/PyArgumentListInspection/propertyReturnsCallable.py new file mode 100644 index 000000000000..a1ac1a17db15 --- /dev/null +++ b/python/testData/inspections/PyArgumentListInspection/propertyReturnsCallable.py @@ -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() +c.f(1, 2, 3) diff --git a/python/testSrc/com/jetbrains/python/PyTypeTest.java b/python/testSrc/com/jetbrains/python/PyTypeTest.java index 121f51422725..ba06be6cced3 100644 --- a/python/testSrc/com/jetbrains/python/PyTypeTest.java +++ b/python/testSrc/com/jetbrains/python/PyTypeTest.java @@ -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(); } diff --git a/python/testSrc/com/jetbrains/python/inspections/PyArgumentListInspectionTest.java b/python/testSrc/com/jetbrains/python/inspections/PyArgumentListInspectionTest.java index b9e9cb53b3d3..e906073074e5 100644 --- a/python/testSrc/com/jetbrains/python/inspections/PyArgumentListInspectionTest.java +++ b/python/testSrc/com/jetbrains/python/inspections/PyArgumentListInspectionTest.java @@ -138,4 +138,9 @@ public class PyArgumentListInspectionTest extends PyTestCase { public void testDecoratedChangedParameters() { doTest(); } + + // PY-9605 + public void testPropertyReturnsCallable() { + doTest(); + } }