Skip to content

Commit

Permalink
Fixed bugs that resulted in false positive errors when using an expre…
Browse files Browse the repository at this point in the history
…ssion of the form `type(A)` as a base class or a metaclass in a class definition. This addresses #9430.
  • Loading branch information
erictraut committed Nov 8, 2024
1 parent b502c03 commit d9e18bb
Show file tree
Hide file tree
Showing 2 changed files with 43 additions and 3 deletions.
24 changes: 23 additions & 1 deletion packages/pyright-internal/src/analyzer/typeEvaluator.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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 =
Expand Down Expand Up @@ -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(
Expand All @@ -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)) {
Expand Down
22 changes: 20 additions & 2 deletions packages/pyright-internal/src/tests/samples/metaclass3.py
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
# This sample tests the detection of metaclass conflicts.

from typing import Protocol


class Meta1(type):
pass
Expand All @@ -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


Expand All @@ -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): ...

0 comments on commit d9e18bb

Please sign in to comment.