mirror of
https://github.com/microsoft/pyright.git
synced 2024-09-11 07:55:56 +03:00
Fixed a bug that resulted in a false positive error when bidirectional type inference was used with a class that defines a __new__
method within a generic class that returns an instance of the class whose type arguments do not match the type parameters. This addresses https://github.com/microsoft/pyright/issues/5404.
This commit is contained in:
parent
c6e0f563f2
commit
a65b45ed45
@ -10635,7 +10635,9 @@ export function createTypeEvaluator(importLookup: ImportLookup, evaluatorOptions
|
||||
/* isTypeArgumentExplicit */ false
|
||||
);
|
||||
|
||||
effectiveExpectedType = applySolvedTypeVars(genericReturnType, tempTypeVarContext);
|
||||
effectiveExpectedType = applySolvedTypeVars(genericReturnType, tempTypeVarContext, {
|
||||
unknownIfNotFound: true,
|
||||
});
|
||||
}
|
||||
} else if (isFunction(effectiveReturnType)) {
|
||||
// If the return type is a callable and the expected type is a union that
|
||||
|
@ -3667,6 +3667,19 @@ class ApplySolvedTypeVarsTransformer extends TypeVarTransformer {
|
||||
if (typeVar.scopeId && this._typeVarContext.hasSolveForScope(typeVar.scopeId)) {
|
||||
let replacement = signatureContext.getTypeVarType(typeVar, !!this._options.useNarrowBoundOnly);
|
||||
|
||||
// If the type is unknown, see if there's a known wide bound that we can use.
|
||||
if (
|
||||
replacement &&
|
||||
isUnknown(replacement) &&
|
||||
!this._options.useNarrowBoundOnly &&
|
||||
this._options.unknownIfNotFound
|
||||
) {
|
||||
const entry = signatureContext.getTypeVar(typeVar);
|
||||
if (entry?.wideBound) {
|
||||
replacement = entry?.wideBound;
|
||||
}
|
||||
}
|
||||
|
||||
// If there was no narrow bound but there is a wide bound that
|
||||
// contains literals or a TypeVar, we'll use the wide bound even if
|
||||
// "useNarrowBoundOnly" is specified.
|
||||
|
39
packages/pyright-internal/src/tests/samples/constructor27.py
Normal file
39
packages/pyright-internal/src/tests/samples/constructor27.py
Normal file
@ -0,0 +1,39 @@
|
||||
# This sample tests a case where a __new__ method in a generic class
|
||||
# returns an instance of the class but with different type arguments
|
||||
# than expected. This is arguably an error case, but pyright needs
|
||||
# to handle it gracefully.
|
||||
|
||||
from __future__ import annotations
|
||||
from typing import Generic, TypeVar
|
||||
|
||||
|
||||
T = TypeVar("T", contravariant=True)
|
||||
S = TypeVar("S", contravariant=True)
|
||||
|
||||
|
||||
class ClassA(Generic[T]):
|
||||
...
|
||||
|
||||
|
||||
class ClassB(Generic[S, T], ClassA[T]):
|
||||
...
|
||||
|
||||
|
||||
class ClassC(ClassB[S, T]):
|
||||
def __new__(cls, subcon: ClassA[S]) -> ClassC[S, list[S]]:
|
||||
...
|
||||
|
||||
|
||||
class ClassD(ClassB[S, T]):
|
||||
def __new__(cls, subcon: ClassA[S]) -> ClassD[S, list[S]]:
|
||||
...
|
||||
|
||||
|
||||
c = ClassA[int]()
|
||||
|
||||
intermediate = ClassC(c)
|
||||
v1 = ClassD(intermediate)
|
||||
reveal_type(v1, expected_text="ClassD[list[int], list[list[int]]]")
|
||||
|
||||
v2 = ClassD(ClassC(c))
|
||||
reveal_type(v2, expected_text="ClassD[list[int], list[list[int]]]")
|
Loading…
Reference in New Issue
Block a user