From d9e18bbf3e1958ebacf18d445b72a6510dee75b6 Mon Sep 17 00:00:00 2001 From: Eric Traut Date: Fri, 8 Nov 2024 15:48:22 -0800 Subject: [PATCH] Fixed bugs that resulted in false positive errors when using an expression of the form `type(A)` as a base class or a metaclass in a class definition. This addresses #9430. --- .../src/analyzer/typeEvaluator.ts | 24 ++++++++++++++++++- .../src/tests/samples/metaclass3.py | 22 +++++++++++++++-- 2 files changed, 43 insertions(+), 3 deletions(-) diff --git a/packages/pyright-internal/src/analyzer/typeEvaluator.ts b/packages/pyright-internal/src/analyzer/typeEvaluator.ts index 78b243b88442..dee1fb647b32 100644 --- a/packages/pyright-internal/src/analyzer/typeEvaluator.ts +++ b/packages/pyright-internal/src/analyzer/typeEvaluator.ts @@ -17072,6 +17072,17 @@ export function createTypeEvaluator( argType = stripTypeFormRecursive(argType); if (!isAnyOrUnknown(argType) && !isUnbound(argType)) { + // If the specified base class is type(T), use the metaclass + // of T if it's known. + if ( + isClass(argType) && + TypeBase.getInstantiableDepth(argType) > 0 && + argType.shared.effectiveMetaclass && + isClass(argType.shared.effectiveMetaclass) + ) { + argType = argType.shared.effectiveMetaclass; + } + if (isMetaclassInstance(argType)) { assert(isClassInstance(argType)); argType = @@ -17539,7 +17550,7 @@ export function createTypeEvaluator( // Determine the effective metaclass. if (metaclassNode) { - const metaclassType = getTypeOfExpression(metaclassNode, exprFlags).type; + let metaclassType = getTypeOfExpression(metaclassNode, exprFlags).type; if (isInstantiableClass(metaclassType) || isUnknown(metaclassType)) { if (requiresSpecialization(metaclassType, { ignorePseudoGeneric: true })) { addDiagnostic( @@ -17549,6 +17560,17 @@ export function createTypeEvaluator( ); } + // If the specified metaclass is type(T), use the metaclass + // of T if it's known. + if ( + TypeBase.getInstantiableDepth(metaclassType) > 0 && + isClass(metaclassType) && + metaclassType.shared.effectiveMetaclass && + isClass(metaclassType.shared.effectiveMetaclass) + ) { + metaclassType = metaclassType.shared.effectiveMetaclass; + } + classType.shared.declaredMetaclass = metaclassType; if (isInstantiableClass(metaclassType)) { if (isEnumMetaclass(metaclassType)) { diff --git a/packages/pyright-internal/src/tests/samples/metaclass3.py b/packages/pyright-internal/src/tests/samples/metaclass3.py index a6d7e2884076..1062e0a2e7bb 100644 --- a/packages/pyright-internal/src/tests/samples/metaclass3.py +++ b/packages/pyright-internal/src/tests/samples/metaclass3.py @@ -1,5 +1,7 @@ # This sample tests the detection of metaclass conflicts. +from typing import Protocol + class Meta1(type): pass @@ -19,7 +21,7 @@ class Base2(metaclass=Meta2): # This should generate an error because the two # metaclasses conflict. -class Foobar1(Base1, Base2): +class Base3(Base1, Base2): pass @@ -39,5 +41,21 @@ class Base5(metaclass=SubMeta3): pass -class Foobar2(Base4, Base5): +class Base6(Base4, Base5): + pass + + +class Meta10(type): ... + + +class Base10(metaclass=Meta10): ... + + +class Proto10(Protocol): ... + + +class Meta11(type(Base10), type(Proto10)): pass + + +class Base11(Base10, Proto10, metaclass=Meta11): ...