diff --git a/packages/pyright-internal/src/analyzer/typeEvaluator.ts b/packages/pyright-internal/src/analyzer/typeEvaluator.ts index 49fbe41178e8..589b9cd2bf84 100644 --- a/packages/pyright-internal/src/analyzer/typeEvaluator.ts +++ b/packages/pyright-internal/src/analyzer/typeEvaluator.ts @@ -207,6 +207,7 @@ import { convertToInstance, convertToInstantiable, convertTypeToParamSpecValue, + derivesFromAnyOrUnknown, derivesFromClassRecursive, derivesFromStdlibClass, doForEachSubtype, @@ -8300,6 +8301,8 @@ export function createTypeEvaluator(importLookup: ImportLookup, evaluatorOptions Localizer.Diagnostic.superCallSecondArg().format({ type: printType(targetClassType) }), node.arguments[1].valueExpression ); + + return { type: UnknownType.create() }; } } else if (enclosingClassType) { bindToType = ClassType.cloneAsInstance(enclosingClassType); @@ -8380,38 +8383,88 @@ export function createTypeEvaluator(importLookup: ImportLookup, evaluatorOptions const lookupResults = bindToType ? lookUpClassMember(bindToType, memberName, MemberAccessFlags.Default, effectiveTargetClass) : undefined; + + let resultType: Type; if (lookupResults && isInstantiableClass(lookupResults.classType)) { - return { - type: resultIsInstance - ? ClassType.cloneAsInstance(lookupResults.classType) - : lookupResults.classType, - bindToSelfType: bindToType - ? TypeBase.cloneForCondition( - synthesizeTypeVarForSelfCls(bindToType, /* isClsParam */ false), - bindToType.condition - ) - : undefined, - }; + resultType = lookupResults.classType; + } else if ( + effectiveTargetClass && + !isAnyOrUnknown(effectiveTargetClass) && + !derivesFromAnyOrUnknown(effectiveTargetClass) && + objectType && + isClassInstance(objectType) + ) { + resultType = ClassType.cloneAsInstantiable(objectType); + } else { + resultType = UnknownType.create(); } + + return { + type: resultIsInstance ? convertToInstance(resultType) : resultType, + bindToSelfType: bindToType + ? TypeBase.cloneForCondition( + synthesizeTypeVarForSelfCls(bindToType, /* isClsParam */ false), + bindToType.condition + ) + : undefined, + }; } - // If the lookup failed, try to return the first base class. An error - // will be reported by the member lookup logic at a later time. + // Handle the super() call when used outside of a member access expression. if (isInstantiableClass(targetClassType)) { - // If the class derives from one or more unknown classes, - // return unknown here to prevent spurious errors. - if (targetClassType.details.mro.some((mroBase) => isAnyOrUnknown(mroBase))) { - return { type: UnknownType.create() }; - } + // We don't know which member is going to be accessed, so we cannot + // deterministically determine the correct type in this case. We'll + // use a heuristic that produces the "correct" (desired) behavior in + // most cases. If there's a bindToType and the targetClassType is one + // of the base classes of the bindToType, we'll return the next base + // class. + if (bindToType) { + let nextBaseClassType: Type | undefined; + + if (ClassType.isSameGenericClass(bindToType, targetClassType)) { + if (bindToType.details.baseClasses.length > 0) { + nextBaseClassType = bindToType.details.baseClasses[0]; + } + } else { + const baseClassIndex = bindToType.details.baseClasses.findIndex( + (baseClass) => + isClass(baseClass) && ClassType.isSameGenericClass(baseClass, targetClassType as ClassType) + ); + + if (baseClassIndex >= 0 && baseClassIndex < bindToType.details.baseClasses.length - 1) { + nextBaseClassType = bindToType.details.baseClasses[baseClassIndex + 1]; + } + } + + if (nextBaseClassType) { + if (isInstantiableClass(nextBaseClassType)) { + nextBaseClassType = specializeForBaseClass(bindToType, nextBaseClassType); + } + return { type: resultIsInstance ? convertToInstance(nextBaseClassType) : nextBaseClassType }; + } - const baseClasses = targetClassType.details.baseClasses; - if (baseClasses.length > 0) { - const baseClassType = baseClasses[0]; - if (isInstantiableClass(baseClassType)) { + // There's not much we can say about the type. Simply return object or type. + if (objectType && isClassInstance(objectType) && typeClassType && isInstantiableClass(typeClassType)) { return { - type: resultIsInstance ? ClassType.cloneAsInstance(baseClassType) : baseClassType, + type: resultIsInstance ? objectType : convertToInstance(typeClassType), }; } + } else { + // If the class derives from one or more unknown classes, + // return unknown here to prevent spurious errors. + if (targetClassType.details.mro.some((mroBase) => isAnyOrUnknown(mroBase))) { + return { type: UnknownType.create() }; + } + + const baseClasses = targetClassType.details.baseClasses; + if (baseClasses.length > 0) { + const baseClassType = baseClasses[0]; + if (isInstantiableClass(baseClassType)) { + return { + type: resultIsInstance ? ClassType.cloneAsInstance(baseClassType) : baseClassType, + }; + } + } } } diff --git a/packages/pyright-internal/src/tests/samples/super7.py b/packages/pyright-internal/src/tests/samples/super7.py index 3b5340792702..8d1987de4fa1 100644 --- a/packages/pyright-internal/src/tests/samples/super7.py +++ b/packages/pyright-internal/src/tests/samples/super7.py @@ -1,6 +1,11 @@ # This sample tests the use of super() with two arguments where the second # argument is an instance. +from typing import Generic, TypeVar + + +T = TypeVar("T") + class BaseClass: def my_method(self, value: int) -> int: @@ -50,3 +55,36 @@ def staticmethod_super_extra_arg(value: int) -> int: # This should generate an error. return super(__class__, self).my_method(self, value) + + +class A(Generic[T]): + ... + + +class B(Generic[T]): + ... + + +class C(A[int], B[T]): + pass + + +c = C[str]() +super_obj_c = super(C, c) +reveal_type(super_obj_c, expected_text="A[int]") + +super_obj_a = super(A, c) +reveal_type(super_obj_a, expected_text="B[str]") + +super_obj_b = super(B, c) +reveal_type(super_obj_b, expected_text="object") + + +super_cls_c = super(C, C) +reveal_type(super_cls_c, expected_text="A[int]") + +super_cls_a = super(A, C) +reveal_type(super_cls_a, expected_text="B[Unknown]") + +super_cls_b = super(B, C) +reveal_type(super_cls_b, expected_text="object")