mirror of
https://github.com/microsoft/pyright.git
synced 2024-09-11 07:55:56 +03:00
Reverted overload change for now. It's causing breaks in the pandas-stubs library. More investigation is needed.
This commit is contained in:
parent
a783677a23
commit
24b612fda2
@ -307,7 +307,7 @@ Some functions or methods can return one of several different types. In cases wh
|
||||
|
||||
4. If more than one overload remains, the “winner” is chosen based on the order in which the overloads are declared. In general, the first remaining overload is the “winner”. There are two exceptions to this rule.
|
||||
Exception 1: When an `*args` (unpacked) argument matches a `*args` parameter in one of the overload signatures, this overrides the normal order-based rule.
|
||||
Exception 2: When two or more overloads match because an argument or a type argument evaluates to `Any` or `Unknown` and this causes overload matching to be ambiguous. In this case, pyright examines the return types of the remaining overloads and eliminates types that are duplicates or are subsumed by (i.e. proper subtypes of) other types in the list. If only one type remains after this coalescing step, that type is used. If more than one type remains after this coalescing step and all of the types are the same class but with different type arguments, these type arguments are “erased” (i.e. replaced with `Unknown`). Otherwise, the type of the call expression evaluates to `Unknown`. For example, if two overloads are matched due to an argument that evaluates to `Any`, and those two overloads have return types of `str` and `LiteralString`, pyright will coalesce this to just `str` because `LiteralString` is a proper subtype of `str`. If the two overloads have return types of `list[str]` and `list[int]`, the call expression will evaluate to `list[Unknown]`. If the two overloads have return types of `str` and `bytes`, the call expression will evaluate to `Unknown` because `str` and `bytes` have no overlap.
|
||||
Exception 2: When two or more overloads match because an argument evaluates to `Any` or `Unknown`, the matching overload is ambiguous. In this case, pyright examines the return types of the remaining overloads and eliminates types that are duplicates or are subsumed by (i.e. proper subtypes of) other types in the list. If only one type remains after this coalescing step, that type is used. If more than one type remains after this coalescing step, the type of the call expression evaluates to `Unknown`. For example, if two overloads are matched due to an argument that evaluates to `Any`, and those two overloads have return types of `str` and `LiteralString`, pyright will coalesce this to just `str` because `LiteralString` is a proper subtype of `str`. If the two overloads have return types of `str` and `bytes`, the call expression will evaluate to `Unknown` because `str` and `bytes` have no overlap.
|
||||
|
||||
5. If no overloads remain, Pyright considers whether any of the arguments are union types. If so, these union types are expanded into their constituent subtypes, and the entire process of overload matching is repeated with the expanded argument types. If two or more overloads match, the union of their respective return types form the final return type for the call expression.
|
||||
|
||||
|
@ -206,7 +206,6 @@ import {
|
||||
doForEachSubtype,
|
||||
ensureFunctionSignaturesAreUnique,
|
||||
explodeGenericClass,
|
||||
getCommonErasedType,
|
||||
getContainerDepth,
|
||||
getDeclaredGeneratorReturnType,
|
||||
getGeneratorTypeArgs,
|
||||
@ -8290,13 +8289,10 @@ export function createTypeEvaluator(importLookup: ImportLookup, evaluatorOptions
|
||||
if (dedupedResultsIncludeAny) {
|
||||
effectiveReturnType = AnyType.create();
|
||||
} else {
|
||||
// If all of the return types are the same generic class,
|
||||
// replace the type arguments with Unknown. Otherwise return
|
||||
// an Unknown type that has associated "possible types" to aid
|
||||
// with completion suggestions.
|
||||
effectiveReturnType =
|
||||
getCommonErasedType(dedupedMatchResults) ??
|
||||
UnknownType.createPossibleType(combinedTypes, possibleMatchInvolvesIncompleteUnknown);
|
||||
effectiveReturnType = UnknownType.createPossibleType(
|
||||
combinedTypes,
|
||||
possibleMatchInvolvesIncompleteUnknown
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
@ -8351,17 +8347,6 @@ export function createTypeEvaluator(importLookup: ImportLookup, evaluatorOptions
|
||||
return matches;
|
||||
}
|
||||
|
||||
// If the relevance of some matches differs, filter out the ones that
|
||||
// are lower relevance. This favors *args parameters in cases where
|
||||
// a *args argument is used.
|
||||
if (matches[0].matchResults.relevance !== matches[matches.length - 1].matchResults.relevance) {
|
||||
matches = matches.filter((m) => m.matchResults.relevance === matches[0].matchResults.relevance);
|
||||
|
||||
if (matches.length < 2) {
|
||||
return matches;
|
||||
}
|
||||
}
|
||||
|
||||
// If all of the return types match, select the first one.
|
||||
if (
|
||||
areTypesSame(
|
||||
@ -8378,11 +8363,9 @@ export function createTypeEvaluator(importLookup: ImportLookup, evaluatorOptions
|
||||
}
|
||||
|
||||
for (let i = 0; i < firstArgResults.length; i++) {
|
||||
// If the arg contains Any or Unknown, see if the corresponding
|
||||
// If the arg is Any or Unknown, see if the corresponding
|
||||
// parameter types differ in any way.
|
||||
const anyOrUnknownInArg = containsAnyOrUnknown(firstArgResults[i].argType, /* recurse */ true);
|
||||
|
||||
if (anyOrUnknownInArg) {
|
||||
if (isAnyOrUnknown(firstArgResults[i].argType)) {
|
||||
const paramTypes = matches.map((match) =>
|
||||
i < match.matchResults.argParams.length
|
||||
? match.matchResults.argParams[i].paramType
|
||||
@ -10480,7 +10463,7 @@ export function createTypeEvaluator(importLookup: ImportLookup, evaluatorOptions
|
||||
let isTypeIncomplete = matchResults.isTypeIncomplete;
|
||||
let argumentErrors = false;
|
||||
let specializedInitSelfType: Type | undefined;
|
||||
let accumulatedAnyOrUnknownArg: UnknownType | AnyType | undefined;
|
||||
let anyOrUnknownArgument: UnknownType | AnyType | undefined;
|
||||
const typeCondition = getTypeCondition(type);
|
||||
|
||||
if (type.boundTypeVarScopeId) {
|
||||
@ -10629,14 +10612,10 @@ export function createTypeEvaluator(importLookup: ImportLookup, evaluatorOptions
|
||||
condition = TypeCondition.combine(condition, argResult.condition) ?? [];
|
||||
}
|
||||
|
||||
// Determine if the argument type contains an Any or Unknown. Accumulate
|
||||
// these across all arguments.
|
||||
const anyOrUnknownInArg = containsAnyOrUnknown(argResult.argType, /* recurs */ true);
|
||||
|
||||
if (anyOrUnknownInArg) {
|
||||
accumulatedAnyOrUnknownArg = accumulatedAnyOrUnknownArg
|
||||
? preserveUnknown(anyOrUnknownInArg, accumulatedAnyOrUnknownArg)
|
||||
: anyOrUnknownInArg;
|
||||
if (isAnyOrUnknown(argResult.argType)) {
|
||||
anyOrUnknownArgument = anyOrUnknownArgument
|
||||
? preserveUnknown(argResult.argType, anyOrUnknownArgument)
|
||||
: argResult.argType;
|
||||
}
|
||||
|
||||
if (type.details.paramSpec) {
|
||||
@ -10791,7 +10770,7 @@ export function createTypeEvaluator(importLookup: ImportLookup, evaluatorOptions
|
||||
return {
|
||||
argumentErrors,
|
||||
argResults,
|
||||
anyOrUnknownArgument: accumulatedAnyOrUnknownArg,
|
||||
anyOrUnknownArgument,
|
||||
returnType: specializedReturnType,
|
||||
isTypeIncomplete,
|
||||
activeParam: matchResults.activeParam,
|
||||
|
@ -2306,40 +2306,6 @@ export function specializeTupleClass(
|
||||
return clonedClassType;
|
||||
}
|
||||
|
||||
// If the array contains two or more class types that are all the same
|
||||
// generic type, return the common "erased" type — a type that
|
||||
// replaces all of the type arguments with Unknown.
|
||||
export function getCommonErasedType(types: Type[]): ClassType | undefined {
|
||||
if (types.length < 2) {
|
||||
return undefined;
|
||||
}
|
||||
|
||||
if (!isClass(types[0])) {
|
||||
return undefined;
|
||||
}
|
||||
|
||||
for (let i = 1; i < types.length; i++) {
|
||||
const candidate = types[i];
|
||||
if (
|
||||
!isClass(candidate) ||
|
||||
TypeBase.isInstance(candidate) !== TypeBase.isInstance(types[0]) ||
|
||||
!ClassType.isSameGenericClass(types[0], candidate)
|
||||
) {
|
||||
return undefined;
|
||||
}
|
||||
}
|
||||
|
||||
if (isTupleClass(types[0])) {
|
||||
return specializeTupleClass(types[0], [{ type: UnknownType.create(), isUnbounded: true }]);
|
||||
}
|
||||
|
||||
return ClassType.cloneForSpecialization(
|
||||
types[0],
|
||||
types[0].details.typeParameters.map((t) => UnknownType.create()),
|
||||
/* isTypeArgumentExplicit */ true
|
||||
);
|
||||
}
|
||||
|
||||
// If the type is a function or overloaded function that has a paramSpec
|
||||
// associated with it and P.args and P.kwargs at the end of the signature,
|
||||
// it removes these parameters from the function.
|
||||
|
@ -1,12 +1,9 @@
|
||||
# This sample tests overload matching in cases where one or more
|
||||
# matches are found due to an Any or Unknown argument.
|
||||
|
||||
from __future__ import annotations
|
||||
from typing import Any, Generic, Literal, TypeVar, overload
|
||||
from typing import Any, Literal, overload
|
||||
from typing_extensions import LiteralString
|
||||
|
||||
_T = TypeVar("_T")
|
||||
|
||||
|
||||
@overload
|
||||
def overload1(x: int, y: float) -> float:
|
||||
@ -123,121 +120,3 @@ def func5(a: Any):
|
||||
reveal_type(overload4(a, flag=False), expected_text="str")
|
||||
reveal_type(overload4("0", flag=a), expected_text="Unknown")
|
||||
|
||||
|
||||
@overload
|
||||
def overload5(x: list[int]) -> list[int]:
|
||||
...
|
||||
|
||||
|
||||
@overload
|
||||
def overload5(x: list[str]) -> list[str]:
|
||||
...
|
||||
|
||||
|
||||
def overload5(x: list[str] | list[int]) -> list[str] | list[int]:
|
||||
return x
|
||||
|
||||
|
||||
def func6(y: list[Any]):
|
||||
reveal_type(overload5(y), expected_text="list[Unknown]")
|
||||
|
||||
|
||||
class ClassA(Generic[_T]):
|
||||
@overload
|
||||
def m1(self: ClassA[int]) -> ClassA[int]:
|
||||
...
|
||||
|
||||
@overload
|
||||
def m1(self: ClassA[str]) -> ClassA[str]:
|
||||
...
|
||||
|
||||
def m1(self) -> ClassA[Any]:
|
||||
return self
|
||||
|
||||
|
||||
def func7(a: ClassA[Any]):
|
||||
reveal_type(a.m1(), expected_text="ClassA[int]")
|
||||
|
||||
|
||||
class ClassB(Generic[_T]):
|
||||
@overload
|
||||
def m1(self: ClassB[int], obj: int | ClassB[int]) -> ClassB[int]:
|
||||
...
|
||||
|
||||
@overload
|
||||
def m1(self: ClassB[str], obj: str | ClassB[str]) -> ClassB[str]:
|
||||
...
|
||||
|
||||
def m1(self, obj: Any) -> ClassB[Any]:
|
||||
return self
|
||||
|
||||
|
||||
def func8(b: ClassB[Any]):
|
||||
reveal_type(b.m1(b), expected_text="ClassB[Unknown]")
|
||||
|
||||
|
||||
_T1 = TypeVar("_T1")
|
||||
_T2 = TypeVar("_T2")
|
||||
|
||||
|
||||
@overload
|
||||
def overload6(a: _T1, /) -> tuple[_T1]:
|
||||
...
|
||||
|
||||
|
||||
@overload
|
||||
def overload6(a: _T1, b: _T2, /) -> tuple[_T1, _T2]:
|
||||
...
|
||||
|
||||
|
||||
@overload
|
||||
def overload6(*args: _T1) -> tuple[_T1, ...]:
|
||||
...
|
||||
|
||||
|
||||
def overload6(*args: Any) -> tuple[Any, ...]:
|
||||
return tuple(args)
|
||||
|
||||
|
||||
def func9(*args: int):
|
||||
reveal_type(overload6(*args), expected_text="tuple[int, ...]")
|
||||
|
||||
|
||||
@overload
|
||||
def overload7(a: float = ..., *, b: Literal[True] = ...) -> float:
|
||||
...
|
||||
|
||||
|
||||
@overload
|
||||
def overload7(a: float = ..., *, b: bool) -> str:
|
||||
...
|
||||
|
||||
|
||||
def overload7(a: float = 1.0, *, b: bool = True) -> float | str:
|
||||
...
|
||||
|
||||
|
||||
def func10(kwargs_dict: dict[Any, Any]):
|
||||
reveal_type(overload7(**kwargs_dict), expected_text="Unknown")
|
||||
|
||||
|
||||
def func11(kwargs_dict: dict[str, Any]):
|
||||
reveal_type(overload7(**kwargs_dict), expected_text="Unknown")
|
||||
|
||||
|
||||
def func12(kwargs_dict: dict[str, bool]):
|
||||
reveal_type(overload7(**kwargs_dict), expected_text="str")
|
||||
|
||||
|
||||
def func13(kwargs_dict: dict[str, Literal[True]]):
|
||||
reveal_type(overload7(**kwargs_dict), expected_text="float")
|
||||
|
||||
|
||||
def func14():
|
||||
reveal_type(overload7(), expected_text="float")
|
||||
|
||||
|
||||
def func15(kwargs_dict: dict[str, str]):
|
||||
# This should generate an error because str isn't a valid type for
|
||||
# the b parameter.
|
||||
overload7(1.0, **kwargs_dict)
|
||||
|
@ -321,7 +321,7 @@ test('Overload11', () => {
|
||||
|
||||
test('Overload12', () => {
|
||||
const analysisResults = TestUtils.typeAnalyzeSampleFiles(['overload12.py']);
|
||||
TestUtils.validateResults(analysisResults, 1);
|
||||
TestUtils.validateResults(analysisResults, 0);
|
||||
});
|
||||
|
||||
test('Overload13', () => {
|
||||
|
Loading…
Reference in New Issue
Block a user