diff --git a/python/python-psi-api/src/com/jetbrains/python/psi/resolve/RatedResolveResult.java b/python/python-psi-api/src/com/jetbrains/python/psi/resolve/RatedResolveResult.java index 3461c987a2a2..9c33888db686 100644 --- a/python/python-psi-api/src/com/jetbrains/python/psi/resolve/RatedResolveResult.java +++ b/python/python-psi-api/src/com/jetbrains/python/psi/resolve/RatedResolveResult.java @@ -61,9 +61,6 @@ public class RatedResolveResult implements ResolveResult { */ public static final int RATE_NORMAL = 0; - @ApiStatus.Experimental - public static final int RATE_LIFTED_PY_FILE_OVERLOAD = -100; - @ApiStatus.Experimental public static final int RATE_PY_FILE_OVERLOAD = -200; diff --git a/python/python-psi-impl/src/com/jetbrains/python/psi/impl/PyCallExpressionHelper.java b/python/python-psi-impl/src/com/jetbrains/python/psi/impl/PyCallExpressionHelper.java index 5b573a3a2fcb..091ac3e8658d 100644 --- a/python/python-psi-impl/src/com/jetbrains/python/psi/impl/PyCallExpressionHelper.java +++ b/python/python-psi-impl/src/com/jetbrains/python/psi/impl/PyCallExpressionHelper.java @@ -170,10 +170,7 @@ public final class PyCallExpressionHelper { final TypeEvalContext context = resolveContext.getTypeEvalContext(); final var results = forEveryScopeTakeOverloadsOtherwiseImplementations( - // Remove the artificial latest overloads from results so as not to spoil their original order - ContainerUtil.filter(subscription.getReference(resolveContext).multiResolve(false), result -> - !(result instanceof RatedResolveResult rrr) || rrr.getRate() != RatedResolveResult.RATE_LIFTED_PY_FILE_OVERLOAD - ), + List.of(subscription.getReference(resolveContext).multiResolve(false)), context ); diff --git a/python/python-psi-impl/src/com/jetbrains/python/psi/impl/references/PyReferenceImpl.java b/python/python-psi-impl/src/com/jetbrains/python/psi/impl/references/PyReferenceImpl.java index 163140b46e97..9b0560c47f97 100644 --- a/python/python-psi-impl/src/com/jetbrains/python/psi/impl/references/PyReferenceImpl.java +++ b/python/python-psi-impl/src/com/jetbrains/python/psi/impl/references/PyReferenceImpl.java @@ -252,13 +252,13 @@ public class PyReferenceImpl implements PsiReferenceEx, PsiPolyVariantReference if (r.getClass() != RatedResolveResult.class || !(r.getElement() instanceof PyFunction pyFunction)) { return StreamEx.of(r); } - int adjustedRate = r.getRate() == RatedResolveResult.RATE_PY_FILE_OVERLOAD ? - RatedResolveResult.RATE_LIFTED_PY_FILE_OVERLOAD : r.getRate(); - return StreamEx.of(PyiUtil.getOverloads(pyFunction, typeEvalContext)) + List overloads = PyiUtil.getOverloads(pyFunction, typeEvalContext); + if (overloads.isEmpty()) { + return StreamEx.of(r); + } + return StreamEx.of(overloads) .map(overload -> new RatedResolveResult(getRate(overload, typeEvalContext), overload)) - .prepend(StreamEx.ofNullable( - PyiUtil.isInsideStub(myElement) ? null : new RatedResolveResult(adjustedRate, pyFunction) - )); + .prepend(StreamEx.ofNullable(PyiUtil.isOverload(pyFunction, typeEvalContext) ? null : r)); }) .toImmutableList(); } diff --git a/python/testData/quickdoc/HoverOverOverloads.html b/python/testData/quickdoc/HoverOverOverloads.html index e9e6f4c0a7c1..bf9ba7a4b22a 100644 --- a/python/testData/quickdoc/HoverOverOverloads.html +++ b/python/testData/quickdoc/HoverOverOverloads.html @@ -1 +1 @@ -@typing.overload
def foo(p: str) -> str \ No newline at end of file +@typing.overload
def foo(p: int) -> int \ No newline at end of file diff --git a/python/testData/quickdoc/Overloads.html b/python/testData/quickdoc/Overloads.html index fdc473a6fce7..3eca99d04eed 100644 --- a/python/testData/quickdoc/Overloads.html +++ b/python/testData/quickdoc/Overloads.html @@ -1 +1 @@ -
 Overloads
@typing.overload
def foo(p: str) -> str
\ No newline at end of file +
 Overloads
@typing.overload
def foo(p: int) -> int
\ No newline at end of file diff --git a/python/testSrc/com/jetbrains/python/Py3ResolveTest.java b/python/testSrc/com/jetbrains/python/Py3ResolveTest.java index 6ebe47013c29..cea425ea65b4 100644 --- a/python/testSrc/com/jetbrains/python/Py3ResolveTest.java +++ b/python/testSrc/com/jetbrains/python/Py3ResolveTest.java @@ -379,15 +379,16 @@ public class Py3ResolveTest extends PyResolveTestCase { // PY-22971 public void testTopLevelOverloadsAndNoImplementation() { - // resolve to the last overload + // resolve to the first overload final PyFunction foo = assertResolvesTo(PyFunction.class, "foo"); final TypeEvalContext context = TypeEvalContext.codeAnalysis(myFixture.getProject(), myFixture.getFile()); + assertTrue(PyiUtil.isOverload(foo, context)); PyiUtil .getOverloads(foo, context) .forEach( overload -> { - if (overload != foo) assertTrue(PyPsiUtils.isBefore(overload, foo)); + if (overload != foo) assertTrue(PyPsiUtils.isBefore(foo, overload)); } ); } @@ -402,7 +403,7 @@ public class Py3ResolveTest extends PyResolveTestCase { // PY-22971 public void testTopLevelOverloadsAndImplementations() { - // resolve to the last overload + // resolve to the first overload because there is no subsequent implementation before the reference final PyFunction foo = assertResolvesTo(PyFunction.class, "foo"); final TypeEvalContext context = TypeEvalContext.codeAnalysis(myFixture.getProject(), myFixture.getFile()); assertTrue(PyiUtil.isOverload(foo, context)); @@ -410,7 +411,7 @@ public class Py3ResolveTest extends PyResolveTestCase { ((PyFile)foo.getContainingFile()) .getTopLevelFunctions() .forEach( - function -> assertTrue(function == foo || PyPsiUtils.isBefore(function, foo)) + function -> assertTrue(function == foo || PyPsiUtils.isBefore(foo, function)) ); } diff --git a/python/testSrc/com/jetbrains/python/PyOverloadsResolutionTest.java b/python/testSrc/com/jetbrains/python/PyOverloadsResolutionTest.java index 87252bf68997..d80ecc8d2811 100644 --- a/python/testSrc/com/jetbrains/python/PyOverloadsResolutionTest.java +++ b/python/testSrc/com/jetbrains/python/PyOverloadsResolutionTest.java @@ -159,6 +159,28 @@ public class PyOverloadsResolutionTest extends PyTestCase { """); } + // PY-77167 + public void testFirstMatchingFunctionOverloadWhenNotFollowedByImplementationInPyFile() { + doTest("str", + """ + from typing import overload + + @overload + def func(x: int) -> int: + pass + + @overload + def func(x: str) -> str: + pass + + @overload + def func(x: object) -> object: + pass + + expr = func("foo") + """); + } + private void doTest(@NotNull String expectedType, @NotNull String text) { myFixture.configureByText(PythonFileType.INSTANCE, text); final PyExpression expr = myFixture.findElementByText("expr", PyExpression.class);