mirror of
https://github.com/microsoft/pyright.git
synced 2024-08-16 11:20:22 +03:00
Added support for a generic method whose "self" parameter is annotated with a bound TypeVar and is then invoked using another bound TypeVar.
This commit is contained in:
parent
04fb613cba
commit
6efb0fa136
@ -224,7 +224,7 @@ interface TypeResult {
|
||||
// as the class or object used to bind the member, but the
|
||||
// "super" call can specify a different class or object to
|
||||
// bind.
|
||||
bindToType?: ClassType | ObjectType;
|
||||
bindToType?: ClassType | ObjectType | TypeVarType;
|
||||
}
|
||||
|
||||
interface EffectiveTypeResult {
|
||||
@ -1160,7 +1160,7 @@ export function createTypeEvaluator(importLookup: ImportLookup, evaluatorOptions
|
||||
usage: EvaluatorUsage,
|
||||
diag: DiagnosticAddendum,
|
||||
memberAccessFlags = MemberAccessFlags.None,
|
||||
bindToType?: ClassType | ObjectType
|
||||
bindToType?: ClassType | ObjectType | TypeVarType
|
||||
): Type | undefined {
|
||||
const memberInfo = getTypeFromClassMemberName(
|
||||
errorNode,
|
||||
@ -3520,6 +3520,7 @@ export function createTypeEvaluator(importLookup: ImportLookup, evaluatorOptions
|
||||
{
|
||||
type: makeTypeVarsConcrete(baseType),
|
||||
node,
|
||||
bindToType: baseType,
|
||||
},
|
||||
usage,
|
||||
EvaluatorFlags.None
|
||||
@ -3739,7 +3740,7 @@ export function createTypeEvaluator(importLookup: ImportLookup, evaluatorOptions
|
||||
usage: EvaluatorUsage,
|
||||
diag: DiagnosticAddendum,
|
||||
flags: MemberAccessFlags,
|
||||
bindToType?: ClassType | ObjectType
|
||||
bindToType?: ClassType | ObjectType | TypeVarType
|
||||
): ClassMemberLookup | undefined {
|
||||
// If this is a special type (like "List") that has an alias class (like
|
||||
// "list"), switch to the alias, which defines the members.
|
||||
@ -3823,7 +3824,8 @@ export function createTypeEvaluator(importLookup: ImportLookup, evaluatorOptions
|
||||
memberInfo,
|
||||
classType,
|
||||
bindToType,
|
||||
(flags & MemberAccessFlags.AccessClassMembersOnly) === 0,
|
||||
/* isAccessedThroughObject */ (flags & MemberAccessFlags.AccessClassMembersOnly) === 0,
|
||||
/* treatAsClassMethod */ bindToType !== undefined && TypeBase.isInstantiable(bindToType),
|
||||
errorNode,
|
||||
memberName,
|
||||
usage,
|
||||
@ -3868,6 +3870,7 @@ export function createTypeEvaluator(importLookup: ImportLookup, evaluatorOptions
|
||||
classType,
|
||||
bindToType,
|
||||
/* isAccessedThroughObject */ false,
|
||||
/* treatAsClassMethod */ bindToType !== undefined && TypeBase.isInstantiable(bindToType),
|
||||
errorNode,
|
||||
memberName,
|
||||
usage,
|
||||
@ -3896,8 +3899,9 @@ export function createTypeEvaluator(importLookup: ImportLookup, evaluatorOptions
|
||||
type: Type,
|
||||
memberInfo: ClassMember | undefined,
|
||||
classType: ClassType,
|
||||
bindToType: ObjectType | ClassType | undefined,
|
||||
bindToType: ObjectType | ClassType | TypeVarType | undefined,
|
||||
isAccessedThroughObject: boolean,
|
||||
treatAsClassMethod: boolean,
|
||||
errorNode: ExpressionNode,
|
||||
memberName: string,
|
||||
usage: EvaluatorUsage,
|
||||
@ -4054,12 +4058,21 @@ export function createTypeEvaluator(importLookup: ImportLookup, evaluatorOptions
|
||||
// assigned to an instance variable), don't perform any binding.
|
||||
const isInstanceMember = isAccessedThroughObject && memberInfo?.isInstanceMember;
|
||||
if (!isInstanceMember) {
|
||||
return bindFunctionToClassOrObject(
|
||||
bindToType || (isAccessedThroughObject ? ObjectType.create(classType) : classType),
|
||||
subtype,
|
||||
/* treatAsClassMethod */ bindToType !== undefined,
|
||||
errorNode
|
||||
);
|
||||
let baseType: ClassType | ObjectType = isAccessedThroughObject
|
||||
? ObjectType.create(classType)
|
||||
: classType;
|
||||
if (bindToType) {
|
||||
if (isTypeVar(bindToType)) {
|
||||
const concreteType = makeTopLevelTypeVarsConcrete(bindToType);
|
||||
if (isClass(concreteType) || isObject(concreteType)) {
|
||||
baseType = concreteType;
|
||||
}
|
||||
} else {
|
||||
baseType = bindToType;
|
||||
}
|
||||
}
|
||||
|
||||
return bindFunctionToClassOrObject(baseType, subtype, treatAsClassMethod, errorNode, bindToType);
|
||||
}
|
||||
}
|
||||
|
||||
@ -16930,7 +16943,7 @@ export function createTypeEvaluator(importLookup: ImportLookup, evaluatorOptions
|
||||
memberType: Type,
|
||||
treatAsClassMethod: boolean,
|
||||
errorNode?: ParseNode,
|
||||
firstParamType?: ClassType | ObjectType
|
||||
firstParamType?: ClassType | ObjectType | TypeVarType
|
||||
): Type | undefined {
|
||||
if (memberType.category === TypeCategory.Function) {
|
||||
// If the caller specified no base type, always strip the
|
||||
@ -16993,7 +17006,7 @@ export function createTypeEvaluator(importLookup: ImportLookup, evaluatorOptions
|
||||
baseType: ClassType | ObjectType,
|
||||
memberType: FunctionType,
|
||||
errorNode: ParseNode | undefined,
|
||||
firstParamType?: ClassType | ObjectType,
|
||||
firstParamType?: ClassType | ObjectType | TypeVarType,
|
||||
stripFirstParam = true
|
||||
): Type | undefined {
|
||||
const classType = isClass(baseType) ? baseType : baseType.classType;
|
||||
|
@ -0,0 +1,16 @@
|
||||
# This sample validates that a generic type can be used to solve a
|
||||
# TypeVar in a generic method where the "self" parameter is itself generic.
|
||||
|
||||
from typing import TypeVar
|
||||
|
||||
_T1 = TypeVar("_T1", bound="P")
|
||||
_T2 = TypeVar("_T2", bound="P")
|
||||
|
||||
|
||||
class P:
|
||||
def chain(self: _T1) -> _T1:
|
||||
...
|
||||
|
||||
|
||||
def func(p1: _T2) -> _T2:
|
||||
return p1.chain()
|
@ -445,6 +445,12 @@ test('GenericTypes40', () => {
|
||||
TestUtils.validateResults(analysisResults, 0);
|
||||
});
|
||||
|
||||
test('GenericTypes41', () => {
|
||||
const analysisResults = TestUtils.typeAnalyzeSampleFiles(['genericTypes41.py']);
|
||||
|
||||
TestUtils.validateResults(analysisResults, 0);
|
||||
});
|
||||
|
||||
test('Protocol1', () => {
|
||||
const analysisResults = TestUtils.typeAnalyzeSampleFiles(['protocol1.py']);
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user