Skip to content

Commit

Permalink
Fixed a bug that resulted in a false positive when an expression of t…
Browse files Browse the repository at this point in the history
…ype `type[Self]` was used as the base type for a member access expression that was then used to call an instance method on that class. This addresses #5530.
  • Loading branch information
msfterictraut committed Jul 18, 2023
1 parent 5be5852 commit d6da783
Show file tree
Hide file tree
Showing 3 changed files with 36 additions and 2 deletions.
18 changes: 16 additions & 2 deletions packages/pyright-internal/src/analyzer/typeEvaluator.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5602,7 +5602,7 @@ export function createTypeEvaluator(importLookup: ImportLookup, evaluatorOptions
type: ClassType.isClassProperty(lookupClass)
? baseTypeClass
: isAccessedThroughObject
? bindToType || ClassType.cloneAsInstance(baseTypeClass)
? bindToType ?? ClassType.cloneAsInstance(baseTypeClass)
: NoneType.createInstance(),
},
},
Expand Down Expand Up @@ -5801,14 +5801,28 @@ export function createTypeEvaluator(importLookup: ImportLookup, evaluatorOptions
// If this function is an instance member (e.g. a lambda that was
// assigned to an instance variable), don't perform any binding.
if (!isAccessedThroughObject || (memberInfo && !memberInfo.isInstanceMember)) {
let effectiveBindToType = bindToType;

if (bindToType && !isInstantiableMetaclass(baseTypeClass)) {
// If bindToType is an instantiable class or TypeVar but we're targeting
// an instance method (in a non-metaclass), we need to convert
// the bindToType to an instance.
const targetMethod = isFunction(concreteSubtype)
? concreteSubtype
: concreteSubtype.overloads[0];
if (FunctionType.isInstanceMethod(targetMethod) && !TypeBase.isInstance(bindToType)) {
effectiveBindToType = convertToInstance(bindToType) as ClassType | TypeVarType;
}
}

return bindFunctionToClassOrObject(
isAccessedThroughObject ? ClassType.cloneAsInstance(baseTypeClass) : baseTypeClass,
concreteSubtype,
memberInfo && isInstantiableClass(memberInfo.classType) ? memberInfo.classType : undefined,
errorNode,
/* recursionCount */ undefined,
treatConstructorAsClassMember,
bindToType
effectiveBindToType
);
}
}
Expand Down
15 changes: 15 additions & 0 deletions packages/pyright-internal/src/tests/samples/memberAccess22.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
# This sample tests the case where a `type[T]` or `type[Self]` typevar is
# used as the base for a member access but is then used to call an
# instance method on the resulting class.

from contextlib import contextmanager


class A:
@classmethod
def method1(cls) -> None:
cls.method2

@contextmanager
def method2(self):
yield
5 changes: 5 additions & 0 deletions packages/pyright-internal/src/tests/typeEvaluator4.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -525,6 +525,11 @@ test('MemberAccess21', () => {
TestUtils.validateResults(analysisResults, 1);
});

test('MemberAccess22', () => {
const analysisResults = TestUtils.typeAnalyzeSampleFiles(['memberAccess22.py']);
TestUtils.validateResults(analysisResults, 0);
});

test('DataClassNamedTuple1', () => {
const analysisResults = TestUtils.typeAnalyzeSampleFiles(['dataclassNamedTuple1.py']);

Expand Down

0 comments on commit d6da783

Please sign in to comment.