mirror of
https://github.com/microsoft/pyright.git
synced 2024-09-11 07:55:56 +03:00
Fixed a bug that results in a false positive error under certain circumstances when a TypeVar or TypeVarTuple is being solved with literal values in both an invariant and non-invariant context. This addresses #7562. (#7629)
This commit is contained in:
parent
4149eb3eda
commit
628e246887
@ -251,7 +251,7 @@ export function assignTypeToTypeVar(
|
||||
if (!curWideTypeBound && !destType.details.isSynthesizedSelf) {
|
||||
curWideTypeBound = destType.details.boundType;
|
||||
}
|
||||
const curNarrowTypeBound = curEntry?.narrowBound;
|
||||
let curNarrowTypeBound = curEntry?.narrowBound;
|
||||
let newNarrowTypeBound = curNarrowTypeBound;
|
||||
let newWideTypeBound = curWideTypeBound;
|
||||
const diagAddendum = diag ? new DiagnosticAddendum() : undefined;
|
||||
@ -383,8 +383,15 @@ export function assignTypeToTypeVar(
|
||||
if (!curNarrowTypeBound || isTypeSame(destType, curNarrowTypeBound)) {
|
||||
// There was previously no narrow bound. We've now established one.
|
||||
newNarrowTypeBound = adjSrcType;
|
||||
} else if (!isTypeSame(curNarrowTypeBound, adjSrcType, {}, recursionCount)) {
|
||||
if (isAnyOrUnknown(adjSrcType) && curEntry.tupleTypes) {
|
||||
} else if (isTypeSame(curNarrowTypeBound, adjSrcType, {}, recursionCount)) {
|
||||
// If this is an invariant context and there is currently no wide type bound
|
||||
// established, use the "no literals" version of the narrow type bounds rather
|
||||
// than a version that has literals.
|
||||
if (!newWideTypeBound && isInvariant && curEntry?.narrowBoundNoLiterals) {
|
||||
newNarrowTypeBound = curEntry.narrowBoundNoLiterals;
|
||||
}
|
||||
} else {
|
||||
if (isAnyOrUnknown(adjSrcType) && curEntry?.tupleTypes) {
|
||||
// Handle the tuple case specially. If Any or Unknown is assigned
|
||||
// during the construction of a tuple, the resulting tuple type must
|
||||
// be tuple[Any, ...], which is compatible with any tuple.
|
||||
@ -474,6 +481,14 @@ export function assignTypeToTypeVar(
|
||||
newNarrowTypeBound = widenedType;
|
||||
} else {
|
||||
const objectType = evaluator.getObjectType();
|
||||
|
||||
// If this is an invariant context and there is currently no wide type bound
|
||||
// established, use the "no literals" version of the narrow type bounds rather
|
||||
// than a version that has literals.
|
||||
if (!newWideTypeBound && isInvariant && curEntry?.narrowBoundNoLiterals) {
|
||||
curNarrowTypeBound = curEntry.narrowBoundNoLiterals;
|
||||
}
|
||||
|
||||
const curSolvedNarrowTypeBound = applySolvedTypeVars(curNarrowTypeBound, typeVarContext);
|
||||
|
||||
// In some extreme edge cases, the narrow type bound can become
|
||||
@ -548,6 +563,10 @@ export function assignTypeToTypeVar(
|
||||
}
|
||||
}
|
||||
|
||||
if (!newWideTypeBound && isInvariant) {
|
||||
newWideTypeBound = newNarrowTypeBound;
|
||||
}
|
||||
|
||||
// If there's a bound type, make sure the source is assignable to it.
|
||||
if (destType.details.boundType) {
|
||||
const updatedType = (newNarrowTypeBound || newWideTypeBound)!;
|
||||
|
@ -10,49 +10,40 @@ T = TypeVar("T")
|
||||
S = TypeVar("S", covariant=True)
|
||||
|
||||
|
||||
class ParentA:
|
||||
...
|
||||
class ParentA: ...
|
||||
|
||||
|
||||
class ChildA(ParentA, Generic[T]):
|
||||
def __init__(self, a: T) -> None:
|
||||
...
|
||||
def __init__(self, a: T) -> None: ...
|
||||
|
||||
|
||||
def func1(arg1: ParentA, arg2: ParentA):
|
||||
...
|
||||
def func1(arg1: ParentA, arg2: ParentA): ...
|
||||
|
||||
|
||||
func1(ChildA(1), ChildA(2))
|
||||
|
||||
|
||||
class ParentB(Generic[T]):
|
||||
...
|
||||
class ParentB(Generic[T]): ...
|
||||
|
||||
|
||||
class ChildB(ParentB[T]):
|
||||
def __init__(self, a: T) -> None:
|
||||
...
|
||||
def __init__(self, a: T) -> None: ...
|
||||
|
||||
|
||||
def func2(arg1: ParentB[T], arg2: ParentB[T]) -> T:
|
||||
...
|
||||
def func2(arg1: ParentB[T], arg2: ParentB[T]) -> T: ...
|
||||
|
||||
|
||||
x1 = func2(ChildB(""), ChildB(1.2))
|
||||
reveal_type(x1, expected_text="str | float")
|
||||
# This should generate an error.
|
||||
func2(ChildB(""), ChildB(1.2))
|
||||
|
||||
|
||||
class ClassC(Generic[S]):
|
||||
def __new__(cls, item: S) -> "ClassC[S]":
|
||||
...
|
||||
def __new__(cls, item: S) -> "ClassC[S]": ...
|
||||
|
||||
def __call__(self, obj: Any) -> S:
|
||||
...
|
||||
def __call__(self, obj: Any) -> S: ...
|
||||
|
||||
|
||||
def func3(func1: Callable[..., T], func2: Callable[..., T]) -> T:
|
||||
...
|
||||
def func3(func1: Callable[..., T], func2: Callable[..., T]) -> T: ...
|
||||
|
||||
|
||||
x2 = func3(ClassC(""), ClassC(1))
|
||||
@ -61,18 +52,14 @@ reveal_type(x2, expected_text="str | int")
|
||||
|
||||
class ClassD(Generic[S]):
|
||||
@overload
|
||||
def __new__(cls, item: S, /) -> ClassD[S]:
|
||||
...
|
||||
def __new__(cls, item: S, /) -> ClassD[S]: ...
|
||||
|
||||
@overload
|
||||
def __new__(cls, item: S, __item2: S, /) -> ClassD[tuple[S, S]]:
|
||||
...
|
||||
def __new__(cls, item: S, __item2: S, /) -> ClassD[tuple[S, S]]: ...
|
||||
|
||||
def __new__(cls, *items: Any) -> Any:
|
||||
...
|
||||
def __new__(cls, *items: Any) -> Any: ...
|
||||
|
||||
def __call__(self, obj: Any) -> Any:
|
||||
...
|
||||
def __call__(self, obj: Any) -> Any: ...
|
||||
|
||||
|
||||
func3(ClassD(""), ClassD(""))
|
||||
|
@ -13,14 +13,13 @@ class NT1(NamedTuple, Generic[_T1]):
|
||||
c: list[_T1]
|
||||
|
||||
|
||||
reveal_type(NT1(3, 4, ["hi"]), expected_text="NT1[str | int]")
|
||||
reveal_type(NT1(3, 4, ["hi"]), expected_text="NT1[int | str]")
|
||||
reveal_type(NT1(3, 4, []), expected_text="NT1[int]")
|
||||
reveal_type(NT1(3.4, 4, [1, 2]), expected_text="NT1[float]")
|
||||
reveal_type(NT1(3.4, 4, [2j]), expected_text="NT1[complex]")
|
||||
|
||||
|
||||
class NT2(NT1[str]):
|
||||
...
|
||||
class NT2(NT1[str]): ...
|
||||
|
||||
|
||||
reveal_type(NT2("", 4, []), expected_text="NT2")
|
||||
|
@ -1,7 +1,7 @@
|
||||
# This sample tests the TypeVar matching logic related to
|
||||
# variadic type variables.
|
||||
|
||||
from typing import Any, Generic, TypeVar
|
||||
from typing import Any, Generic, Literal, TypeVar, overload
|
||||
from typing_extensions import ( # pyright: ignore[reportMissingModuleSource]
|
||||
TypeVarTuple,
|
||||
Unpack,
|
||||
@ -85,3 +85,15 @@ def func2(p1: tuple[str, int], p2: list[str]):
|
||||
def func3(x: Array[Unpack[_Xs]]) -> Array[Unpack[_Xs]]:
|
||||
y: Array[Unpack[tuple[Any, ...]]] = x
|
||||
return x
|
||||
|
||||
|
||||
@overload
|
||||
def func4(signal: Array[*_Xs], *args: *_Xs) -> None: ...
|
||||
@overload
|
||||
def func4(signal: str, *args: Any) -> None: ...
|
||||
def func4(signal: Array[*_Xs] | str, *args: *_Xs) -> None: ...
|
||||
|
||||
|
||||
def func5(a1: Array[Literal["a", "b"]], a2: Array[Literal["a"], Literal["b"]]):
|
||||
func4(a1, "a")
|
||||
func4(a2, "a", "b")
|
||||
|
@ -1674,7 +1674,7 @@ test('Constructor27', () => {
|
||||
test('Constructor28', () => {
|
||||
const analysisResults = TestUtils.typeAnalyzeSampleFiles(['constructor28.py']);
|
||||
|
||||
TestUtils.validateResults(analysisResults, 0);
|
||||
TestUtils.validateResults(analysisResults, 1);
|
||||
});
|
||||
|
||||
test('Constructor29', () => {
|
||||
|
Loading…
Reference in New Issue
Block a user