Improved coalescing of overload return types when the result is ambiguous due to Any or Unknown arguments.

This commit is contained in:
Eric Traut 2022-12-17 00:20:44 -08:00
parent 1dc84b93b1
commit c9e43d2594
2 changed files with 84 additions and 16 deletions

View File

@ -7751,11 +7751,33 @@ export function createTypeEvaluator(importLookup: ImportLookup, evaluatorOptions
// Unknown, but include the "possible types" to allow for completion
// suggestions.
if (!isDefinitiveMatchFound) {
if (possibleMatchResults.length > 1) {
returnTypes.push(UnknownType.createPossibleType(combineTypes(possibleMatchResults)));
} else {
returnTypes.push(possibleMatchResults[0]);
}
// Eliminate any return types that are subsumed by other return types.
let dedupedMatchResults: Type[] = [];
possibleMatchResults.forEach((subtype) => {
let isSubtypeSubsumed = false;
for (let dedupedIndex = 0; dedupedIndex < dedupedMatchResults.length; dedupedIndex++) {
if (assignType(dedupedMatchResults[dedupedIndex], subtype)) {
isSubtypeSubsumed = true;
break;
} else if (assignType(subtype, dedupedMatchResults[dedupedIndex])) {
dedupedMatchResults[dedupedIndex] = NeverType.createNever();
break;
}
}
if (!isSubtypeSubsumed) {
dedupedMatchResults.push(subtype);
}
});
dedupedMatchResults = dedupedMatchResults.filter((t) => !isNever(t));
const combinedTypes = combineTypes(dedupedMatchResults);
returnTypes.push(
dedupedMatchResults.length > 1 ? UnknownType.createPossibleType(combinedTypes) : combinedTypes
);
}
if (!matchedOverload) {

View File

@ -2,30 +2,76 @@
# matches are found due to an Any or Unknown argument.
from typing import Any, overload
from typing_extensions import LiteralString
@overload
def func1(x: int, y: float) -> float:
def overload1(x: int, y: float) -> float:
...
@overload
def func1(x: str, y: float) -> str:
def overload1(x: str, y: float) -> str:
...
def func1(x: str | int, y: float) -> float | str:
def overload1(x: str | int, y: float) -> float | str:
...
def func1(a: Any):
v1 = overload1(1, 3.4)
reveal_type(v1, expected_text="float")
v2 = overload1("", 3.4)
reveal_type(v2, expected_text="str")
v3 = overload1(a, 3.4)
reveal_type(v3, expected_text="Unknown")
v4 = overload1("", a)
reveal_type(v4, expected_text="str")
@overload
def overload2(x: int) -> Any:
...
@overload
def overload2(x: str) -> str:
...
def overload2(x: str | int) -> Any | str:
...
def func2(a: Any):
v1 = func1(1, 3.4)
reveal_type(v1, expected_text="float")
v1 = overload2("")
reveal_type(v1, expected_text="str")
v2 = func1("", 3.4)
v2 = overload2(3)
reveal_type(v2, expected_text="Any")
v3 = overload2(a)
reveal_type(v3, expected_text="Any")
@overload
def overload3(x: LiteralString) -> LiteralString:
...
@overload
def overload3(x: str) -> str:
...
def overload3(x: str) -> str:
...
def func3(a: Any, b: str):
v1 = overload3("")
reveal_type(v1, expected_text="LiteralString")
v2 = overload3(b)
reveal_type(v2, expected_text="str")
v3 = func1(a, 3.4)
reveal_type(v3, expected_text="Unknown")
v4 = func1("", a)
reveal_type(v4, expected_text="str")
v3 = overload3(a)
reveal_type(v3, expected_text="str")