mirror of
https://gitflic.ru/project/openide/openide.git
synced 2026-09-27 10:03:11 +07:00
PY-77167 Simplify resolve logic for overloads, get rid of RatedResolveResult#RATE_LIFTED_PY_FILE_OVERLOAD
If there is an overload not followed by an implementation, which is already an error, always resolve to the first overload, regardless of whether it's a .py file, or a .pyi stub. It allows us to eliminate the special RatedResolveResult#RATE_LIFTED_PY_FILE_OVERLOAD rate in .py files, because we no longer need to duplicate the last, closest reachable overload (normally an implementation should be reachable) with a higher priority, and then filter it out during overload resolution. Meanwhile, this filtering out didn't work right before because some type inference logic, e.g., PyCallExpressionHelper.getCalleeType used in PyReferenceExpressionImpl.getCallableType bypassed it. It should have been done at the level of PyCallExpressionHelper.forEveryScopeTakeOverloadsOtherwiseImplementations. GitOrigin-RevId: 99a624ab85957d7a2d3c2c0ced596e472f9d615b
This commit is contained in:
committed by
intellij-monorepo-bot
parent
97ad29d6c8
commit
414397c7ea
@@ -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;
|
||||
|
||||
|
||||
+1
-4
@@ -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
|
||||
);
|
||||
|
||||
|
||||
+6
-6
@@ -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<PyFunction> 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();
|
||||
}
|
||||
|
||||
@@ -1 +1 @@
|
||||
<span style="color:#808000;">@typing.overload</span><br/><span style="color:#000080;font-weight:bold;">def </span><span style="color:#000000;">foo</span><span style="">(</span><span style="color:#000000;">p</span><span style="">: </span><span style="color:#000000;"><span style="color:#000080;"><a href="psi_element://#typename#str">str</a></span></span><span style="">)</span> -> <span style="color:#000000;"><span style="color:#000080;"><a href="psi_element://#typename#str">str</a></span></span>
|
||||
<span style="color:#808000;">@typing.overload</span><br/><span style="color:#000080;font-weight:bold;">def </span><span style="color:#000000;">foo</span><span style="">(</span><span style="color:#000000;">p</span><span style="">: </span><span style="color:#000000;"><span style="color:#000080;"><a href="psi_element://#typename#int">int</a></span></span><span style="">)</span> -> <span style="color:#000000;"><span style="color:#000080;"><a href="psi_element://#typename#int">int</a></span></span>
|
||||
@@ -1 +1 @@
|
||||
<html><body><div class="bottom"><icon src="AllIcons.Nodes.Package"/> <code><a href="psi_element://#module#Overloads">Overloads</a></code></div><div class="definition"><pre><span style="color:#808000;">@typing.overload</span><br/><span style="color:#000080;font-weight:bold;">def </span><span style="color:#000000;">foo</span><span style="">(</span><span style="color:#000000;">p</span><span style="">: </span><span style="color:#000000;"><span style="color:#000080;"><a href="psi_element://#typename#str">str</a></span></span><span style="">)</span> -> <span style="color:#000000;"><span style="color:#000080;"><a href="psi_element://#typename#str">str</a></span></span></pre></div></body></html>
|
||||
<html><body><div class="bottom"><icon src="AllIcons.Nodes.Package"/> <code><a href="psi_element://#module#Overloads">Overloads</a></code></div><div class="definition"><pre><span style="color:#808000;">@typing.overload</span><br/><span style="color:#000080;font-weight:bold;">def </span><span style="color:#000000;">foo</span><span style="">(</span><span style="color:#000000;">p</span><span style="">: </span><span style="color:#000000;"><span style="color:#000080;"><a href="psi_element://#typename#int">int</a></span></span><span style="">)</span> -> <span style="color:#000000;"><span style="color:#000080;"><a href="psi_element://#typename#int">int</a></span></span></pre></div></body></html>
|
||||
@@ -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))
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
@@ -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);
|
||||
|
||||
Reference in New Issue
Block a user