diff --git a/python/src/com/jetbrains/python/psi/impl/PyCallExpressionHelper.java b/python/src/com/jetbrains/python/psi/impl/PyCallExpressionHelper.java index ff563cd79988..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); @@ -408,6 +421,12 @@ public class PyCallExpressionHelper { if (t != null && !(t instanceof PyNoneType)) { return t; } + if (cls != null && t == null) { + final PyFunction newMethod = cls.findMethodByName(PyNames.NEW, true); + if (newMethod != null && !PyBuiltinCache.getInstance(call).hasInBuiltins(newMethod)) { + return PyUnionType.createWeakType(new PyClassTypeImpl(cls, false)); + } + } } if (cls != null) { return new PyClassTypeImpl(cls, false); diff --git a/python/src/com/jetbrains/python/psi/impl/PyNamedParameterImpl.java b/python/src/com/jetbrains/python/psi/impl/PyNamedParameterImpl.java index f3b2043003ea..22678d6bc80c 100644 --- a/python/src/com/jetbrains/python/psi/impl/PyNamedParameterImpl.java +++ b/python/src/com/jetbrains/python/psi/impl/PyNamedParameterImpl.java @@ -165,7 +165,26 @@ public class PyNamedParameterImpl extends PyPresentableElementImpl) +c.f(1, 2, 3) diff --git a/python/testData/inspections/PyUnresolvedReferencesInspection/CustomNewReturnInAnotherModule/a.py b/python/testData/inspections/PyUnresolvedReferencesInspection/CustomNewReturnInAnotherModule/a.py new file mode 100644 index 000000000000..5e6d4a745397 --- /dev/null +++ b/python/testData/inspections/PyUnresolvedReferencesInspection/CustomNewReturnInAnotherModule/a.py @@ -0,0 +1,4 @@ +from b import C + +c = C() +c.foo() diff --git a/python/testData/inspections/PyUnresolvedReferencesInspection/CustomNewReturnInAnotherModule/b.py b/python/testData/inspections/PyUnresolvedReferencesInspection/CustomNewReturnInAnotherModule/b.py new file mode 100644 index 000000000000..16af06a7831e --- /dev/null +++ b/python/testData/inspections/PyUnresolvedReferencesInspection/CustomNewReturnInAnotherModule/b.py @@ -0,0 +1,8 @@ +class C(object): + def __new__(cls): + return D() + + +class D(object): + def foo(self): + pass diff --git a/python/testSrc/com/jetbrains/python/PyTypeTest.java b/python/testSrc/com/jetbrains/python/PyTypeTest.java index 5d9e84b6b3fc..ba06be6cced3 100644 --- a/python/testSrc/com/jetbrains/python/PyTypeTest.java +++ b/python/testSrc/com/jetbrains/python/PyTypeTest.java @@ -760,6 +760,30 @@ public class PyTypeTest extends PyTestCase { " print(expr)\n"); } + // PY-8953 + public void testSelfInDocString() { + doTest("int", + "class C(object):\n" + + " def foo(self):\n" + + " '''\n" + + " :type self: int\n" + + " '''\n" + + " 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(); + } } diff --git a/python/testSrc/com/jetbrains/python/inspections/PyUnresolvedReferencesInspectionTest.java b/python/testSrc/com/jetbrains/python/inspections/PyUnresolvedReferencesInspectionTest.java index 363d4c0c7242..771c4426fe84 100644 --- a/python/testSrc/com/jetbrains/python/inspections/PyUnresolvedReferencesInspectionTest.java +++ b/python/testSrc/com/jetbrains/python/inspections/PyUnresolvedReferencesInspectionTest.java @@ -298,6 +298,11 @@ public class PyUnresolvedReferencesInspectionTest extends PyTestCase { doMultiFileTest("a.py"); } + // PY-10893 + public void testCustomNewReturnInAnotherModule() { + doMultiFileTest("a.py"); + } + private void doTest() { myFixture.configureByFile(TEST_DIRECTORY + getTestName(true) + ".py"); myFixture.enableInspections(PyUnresolvedReferencesInspection.class);