Fixed false positive error caused by inappropriate method binding for instance methods.

This commit is contained in:
Eric Traut 2021-08-11 11:53:23 -07:00
parent fbb0d521ee
commit bf8b5511d3
4 changed files with 47 additions and 5 deletions

View File

@ -5009,7 +5009,7 @@ export function createTypeEvaluator(
memberInfo,
classType,
bindToType,
/* isAccessedThroughObject */ false,
/* isAccessedThroughObject */ !!bindToType,
flags,
errorNode,
memberName,
@ -5217,7 +5217,7 @@ export function createTypeEvaluator(
} else if (isFunction(subtype) || isOverloadedFunction(subtype)) {
// 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?.isInstanceMember) {
if (!isAccessedThroughObject || (memberInfo && !memberInfo.isInstanceMember)) {
return bindFunctionToClassOrObject(
isAccessedThroughObject ? ClassType.cloneAsInstance(baseTypeClass) : baseTypeClass,
subtype,
@ -11783,7 +11783,7 @@ export function createTypeEvaluator(
function createCallableType(typeArgs: TypeResult[] | undefined, errorNode: ParseNode): FunctionType {
// Create a new function that is marked as "static" so there is later
// no attempt to bind it as though it's an instance or class method.
const functionType = FunctionType.createInstantiable('', '', '', FunctionTypeFlags.StaticMethod);
const functionType = FunctionType.createInstantiable('', '', '', FunctionTypeFlags.None);
TypeBase.setNonCallable(functionType);
functionType.details.declaredReturnType = UnknownType.create();
@ -22807,7 +22807,7 @@ export function createTypeEvaluator(
'__new__',
'',
'',
FunctionTypeFlags.StaticMethod | FunctionTypeFlags.ConstructorMethod | FunctionTypeFlags.SynthesizedMethod
FunctionTypeFlags.ConstructorMethod | FunctionTypeFlags.SynthesizedMethod
);
constructorFunction.details.declaredReturnType = ClassType.cloneAsInstance(classType);
FunctionType.addDefaultParameters(constructorFunction);

View File

@ -0,0 +1,37 @@
# This sample tests that methods are bound properly regardless of
# whether they are decorated.
from typing import Callable
UnboundMethodThatTakesIntAndReturnsStr = Callable[["MyClass", int], str]
def method_decorator(
method: UnboundMethodThatTakesIntAndReturnsStr,
) -> UnboundMethodThatTakesIntAndReturnsStr:
def wrapper(self: "MyClass", a: int) -> str:
return "wrapped " + method(self, a)
return wrapper
class MyClass:
def __init__(self):
self.method4 = lambda x: x
@method_decorator
def method1(self, a: int) -> str:
return "foo"
def method2(self, a: int) -> str:
return "foo"
method3 = method_decorator(method2)
mc = MyClass()
mc.method1(1)
mc.method2(1)
mc.method3(1)
mc.method4(1)

View File

@ -8,7 +8,7 @@ class ClassA:
def __init__(self):
return
def __getattr__(self) -> Callable[[str], str]:
def __getattr__(self, key: str) -> Callable[[str], str]:
return lambda a: a

View File

@ -375,6 +375,11 @@ test('MemberAccess10', () => {
TestUtils.validateResults(analysisResults, 2);
});
test('MemberAccess11', () => {
const analysisResults = TestUtils.typeAnalyzeSampleFiles(['memberAccess11.py']);
TestUtils.validateResults(analysisResults, 0);
});
test('DataClass1', () => {
const analysisResults = TestUtils.typeAnalyzeSampleFiles(['dataclass1.py']);