From f3cb07ce758c13022ca48a03e0edfabfa2f1c080 Mon Sep 17 00:00:00 2001 From: Claude Date: Sat, 28 Feb 2026 00:09:28 +0000 Subject: [PATCH 1/3] [ty] Emit `invalid-metaclass` for parameterized generic metaclasses Generic metaclasses are not supported per the typing spec. This adds a check that rejects `metaclass=SomeGeneric[T]` with an `invalid-metaclass` error when the metaclass argument is a parameterized generic class. https://claude.ai/code/session_01Nhdsupjg85NjKVFHSTZR9x --- .../resources/mdtest/metaclass.md | 15 +++++++++++++++ crates/ty_python_semantic/src/types/class.rs | 11 +++++++++++ .../ty_python_semantic/src/types/infer/builder.rs | 10 ++++++++++ 3 files changed, 36 insertions(+) diff --git a/crates/ty_python_semantic/resources/mdtest/metaclass.md b/crates/ty_python_semantic/resources/mdtest/metaclass.md index 3c4762fe8fefe..d630e14a18429 100644 --- a/crates/ty_python_semantic/resources/mdtest/metaclass.md +++ b/crates/ty_python_semantic/resources/mdtest/metaclass.md @@ -247,6 +247,21 @@ class A[T: str](metaclass=M): ... reveal_type(A.__class__) # revealed: ``` +## Generic metaclass + +Generic metaclasses are not supported. + +```py +from typing import TypeVar, Generic + +T = TypeVar("T") + +class GenericMeta(type, Generic[T]): ... + +# error: [invalid-metaclass] +class GenericMetaInstance(metaclass=GenericMeta[T]): ... +``` + ## Metaclasses of metaclasses ```py diff --git a/crates/ty_python_semantic/src/types/class.rs b/crates/ty_python_semantic/src/types/class.rs index 2316040ea8878..a6644ecc24161 100644 --- a/crates/ty_python_semantic/src/types/class.rs +++ b/crates/ty_python_semantic/src/types/class.rs @@ -2883,6 +2883,15 @@ impl<'db> StaticClassLiteral<'db> { let module = parsed_module(db, self.file(db)).load(db); let explicit_metaclass = self.explicit_metaclass(db, &module); + + // Generic metaclasses are not supported. + // See: https://typing.python.org/en/latest/spec/generics.html#generic-metaclasses + if let Some(Type::GenericAlias(alias)) = explicit_metaclass { + return Err(MetaclassError { + kind: MetaclassErrorKind::GenericMetaclass(Type::GenericAlias(alias)), + }); + } + let (metaclass, class_metaclass_was_from) = if let Some(metaclass) = explicit_metaclass { (metaclass, self) } else if let Some(base_class) = base_classes.next() { @@ -8276,6 +8285,8 @@ pub(super) enum MetaclassErrorKind<'db> { /// inferred metaclass of a base class. This helps us give better error messages in diagnostics. candidate1_is_base_class: bool, }, + /// The metaclass is a parameterized generic class, which is not supported. + GenericMetaclass(Type<'db>), /// The metaclass is not callable NotCallable(Type<'db>), /// The metaclass is of a union type whose some members are not callable diff --git a/crates/ty_python_semantic/src/types/infer/builder.rs b/crates/ty_python_semantic/src/types/infer/builder.rs index 9d1c6670c624d..12c07b79f701b 100644 --- a/crates/ty_python_semantic/src/types/infer/builder.rs +++ b/crates/ty_python_semantic/src/types/infer/builder.rs @@ -1222,6 +1222,16 @@ impl<'db, 'ast> TypeInferenceBuilder<'db, 'ast> { )); } } + MetaclassErrorKind::GenericMetaclass(ty) => { + if let Some(builder) = + self.context.report_lint(&INVALID_METACLASS, class_node) + { + builder.into_diagnostic(format_args!( + "Cannot use a parameterized generic class `{}` as a metaclass", + ty.display(self.db()) + )); + } + } MetaclassErrorKind::NotCallable(ty) => { if let Some(builder) = self.context.report_lint(&INVALID_METACLASS, class_node) From 38aef54b16079ea2f92c00873dc1608ca6b7f8f1 Mon Sep 17 00:00:00 2001 From: Claude Date: Sat, 28 Feb 2026 00:33:31 +0000 Subject: [PATCH 2/3] [ty] Emit `invalid-metaclass` for generic metaclasses with type variable parameters Generic metaclasses parameterized by type variables are not supported per the typing spec. `metaclass=Meta[int]` (fully specialized) is fine, but `metaclass=Meta[T]` (parameterized by type variables) is not. This adds a check in `try_metaclass` that detects when the explicit metaclass is a `GenericAlias` whose specialization contains type variables, and emits an `invalid-metaclass` diagnostic. https://claude.ai/code/session_01Nhdsupjg85NjKVFHSTZR9x --- .../resources/mdtest/metaclass.md | 39 ++++++++++++++++++- crates/ty_python_semantic/src/types/class.rs | 18 ++++++--- 2 files changed, 51 insertions(+), 6 deletions(-) diff --git a/crates/ty_python_semantic/resources/mdtest/metaclass.md b/crates/ty_python_semantic/resources/mdtest/metaclass.md index d630e14a18429..fb7ac6f632ab4 100644 --- a/crates/ty_python_semantic/resources/mdtest/metaclass.md +++ b/crates/ty_python_semantic/resources/mdtest/metaclass.md @@ -249,7 +249,27 @@ reveal_type(A.__class__) # revealed: ## Generic metaclass -Generic metaclasses are not supported. +### Fully specialized + +A generic metaclass fully specialized with concrete types is fine: + +```toml +[environment] +python-version = "3.13" +``` + +```py +class Foo[T](type): + x: T + +class Bar(metaclass=Foo[int]): ... + +reveal_type(Bar.__class__) # revealed: +``` + +### Parameterized by type variables (legacy) + +A generic metaclass parameterized by type variables is not supported: ```py from typing import TypeVar, Generic @@ -262,6 +282,23 @@ class GenericMeta(type, Generic[T]): ... class GenericMetaInstance(metaclass=GenericMeta[T]): ... ``` +### Parameterized by type variables (PEP 695) + +The same applies using PEP 695 syntax: + +```toml +[environment] +python-version = "3.13" +``` + +```py +class Foo[T](type): + x: T + +# error: [invalid-metaclass] +class Bar[T](metaclass=Foo[T]): ... +``` + ## Metaclasses of metaclasses ```py diff --git a/crates/ty_python_semantic/src/types/class.rs b/crates/ty_python_semantic/src/types/class.rs index a6644ecc24161..a4bdbac28e416 100644 --- a/crates/ty_python_semantic/src/types/class.rs +++ b/crates/ty_python_semantic/src/types/class.rs @@ -2884,12 +2884,20 @@ impl<'db> StaticClassLiteral<'db> { let explicit_metaclass = self.explicit_metaclass(db, &module); - // Generic metaclasses are not supported. + // Generic metaclasses parameterized by type variables are not supported. + // `metaclass=Meta[int]` is fine, but `metaclass=Meta[T]` is not. // See: https://typing.python.org/en/latest/spec/generics.html#generic-metaclasses - if let Some(Type::GenericAlias(alias)) = explicit_metaclass { - return Err(MetaclassError { - kind: MetaclassErrorKind::GenericMetaclass(Type::GenericAlias(alias)), - }); + if let Some(ty @ Type::GenericAlias(alias)) = explicit_metaclass { + let specialization_has_typevars = alias + .specialization(db) + .types(db) + .iter() + .any(|ty| ty.has_typevar_or_typevar_instance(db)); + if specialization_has_typevars { + return Err(MetaclassError { + kind: MetaclassErrorKind::GenericMetaclass(ty), + }); + } } let (metaclass, class_metaclass_was_from) = if let Some(metaclass) = explicit_metaclass { From 4ce2e9c55a093badbd7414ee81d37ae8ab874088 Mon Sep 17 00:00:00 2001 From: Claude Date: Sat, 28 Feb 2026 00:59:09 +0000 Subject: [PATCH 3/3] Simplify diagnostic message to "Generic metaclasses are not supported" https://claude.ai/code/session_01Nhdsupjg85NjKVFHSTZR9x --- crates/ty_python_semantic/resources/mdtest/metaclass.md | 2 +- crates/ty_python_semantic/src/types/class.rs | 6 +++--- crates/ty_python_semantic/src/types/infer/builder.rs | 7 ++----- 3 files changed, 6 insertions(+), 9 deletions(-) diff --git a/crates/ty_python_semantic/resources/mdtest/metaclass.md b/crates/ty_python_semantic/resources/mdtest/metaclass.md index fb7ac6f632ab4..2a05f779f1061 100644 --- a/crates/ty_python_semantic/resources/mdtest/metaclass.md +++ b/crates/ty_python_semantic/resources/mdtest/metaclass.md @@ -278,7 +278,7 @@ T = TypeVar("T") class GenericMeta(type, Generic[T]): ... -# error: [invalid-metaclass] +# error: [invalid-metaclass] "Generic metaclasses are not supported" class GenericMetaInstance(metaclass=GenericMeta[T]): ... ``` diff --git a/crates/ty_python_semantic/src/types/class.rs b/crates/ty_python_semantic/src/types/class.rs index a4bdbac28e416..6532df131e769 100644 --- a/crates/ty_python_semantic/src/types/class.rs +++ b/crates/ty_python_semantic/src/types/class.rs @@ -2887,7 +2887,7 @@ impl<'db> StaticClassLiteral<'db> { // Generic metaclasses parameterized by type variables are not supported. // `metaclass=Meta[int]` is fine, but `metaclass=Meta[T]` is not. // See: https://typing.python.org/en/latest/spec/generics.html#generic-metaclasses - if let Some(ty @ Type::GenericAlias(alias)) = explicit_metaclass { + if let Some(Type::GenericAlias(alias)) = explicit_metaclass { let specialization_has_typevars = alias .specialization(db) .types(db) @@ -2895,7 +2895,7 @@ impl<'db> StaticClassLiteral<'db> { .any(|ty| ty.has_typevar_or_typevar_instance(db)); if specialization_has_typevars { return Err(MetaclassError { - kind: MetaclassErrorKind::GenericMetaclass(ty), + kind: MetaclassErrorKind::GenericMetaclass, }); } } @@ -8294,7 +8294,7 @@ pub(super) enum MetaclassErrorKind<'db> { candidate1_is_base_class: bool, }, /// The metaclass is a parameterized generic class, which is not supported. - GenericMetaclass(Type<'db>), + GenericMetaclass, /// The metaclass is not callable NotCallable(Type<'db>), /// The metaclass is of a union type whose some members are not callable diff --git a/crates/ty_python_semantic/src/types/infer/builder.rs b/crates/ty_python_semantic/src/types/infer/builder.rs index 12c07b79f701b..1d89d403771a3 100644 --- a/crates/ty_python_semantic/src/types/infer/builder.rs +++ b/crates/ty_python_semantic/src/types/infer/builder.rs @@ -1222,14 +1222,11 @@ impl<'db, 'ast> TypeInferenceBuilder<'db, 'ast> { )); } } - MetaclassErrorKind::GenericMetaclass(ty) => { + MetaclassErrorKind::GenericMetaclass => { if let Some(builder) = self.context.report_lint(&INVALID_METACLASS, class_node) { - builder.into_diagnostic(format_args!( - "Cannot use a parameterized generic class `{}` as a metaclass", - ty.display(self.db()) - )); + builder.into_diagnostic("Generic metaclasses are not supported"); } } MetaclassErrorKind::NotCallable(ty) => {