@@ -20,8 +20,8 @@ use crate::types::bound_super::BoundSuperError;
2020use crate :: types:: constraints:: { ConstraintSet , IteratorConstraintsExtension } ;
2121use crate :: types:: context:: InferContext ;
2222use crate :: types:: diagnostic:: {
23- DUPLICATE_BASE , INCONSISTENT_MRO , INVALID_BASE , INVALID_DATACLASS_OVERRIDE ,
24- INVALID_TYPE_ALIAS_TYPE , SUPER_CALL_IN_NAMED_TUPLE_METHOD ,
23+ CYCLIC_CLASS_DEFINITION , DUPLICATE_BASE , INCONSISTENT_MRO , INVALID_BASE ,
24+ INVALID_DATACLASS_OVERRIDE , INVALID_TYPE_ALIAS_TYPE , SUPER_CALL_IN_NAMED_TUPLE_METHOD ,
2525 report_conflicting_metaclass_from_bases,
2626} ;
2727use crate :: types:: enums:: {
@@ -92,14 +92,6 @@ fn static_class_explicit_bases_cycle_initial<'db>(
9292 Box :: default ( )
9393}
9494
95- fn inheritance_cycle_initial < ' db > (
96- _db : & ' db dyn Db ,
97- _id : salsa:: Id ,
98- _self : StaticClassLiteral < ' db > ,
99- ) -> Option < InheritanceCycle > {
100- None
101- }
102-
10395fn implicit_attribute_initial < ' db > (
10496 _db : & ' db dyn Db ,
10597 id : salsa:: Id ,
@@ -151,24 +143,6 @@ fn try_metaclass_cycle_initial<'db>(
151143 } )
152144}
153145
154- fn decorators_cycle_initial < ' db > (
155- _db : & ' db dyn Db ,
156- _id : salsa:: Id ,
157- _self : StaticClassLiteral < ' db > ,
158- ) -> Box < [ Type < ' db > ] > {
159- Box :: default ( )
160- }
161-
162- fn fields_cycle_initial < ' db > (
163- _db : & ' db dyn Db ,
164- _id : salsa:: Id ,
165- _self : StaticClassLiteral < ' db > ,
166- _specialization : Option < Specialization < ' db > > ,
167- _field_policy : CodeGeneratorKind < ' db > ,
168- ) -> FxIndexMap < Name , Field < ' db > > {
169- FxIndexMap :: default ( )
170- }
171-
172146fn dynamic_class_explicit_bases_cycle_initial < ' db > (
173147 _db : & ' db dyn Db ,
174148 _id : salsa:: Id ,
@@ -5114,6 +5088,8 @@ impl<'db> DynamicClassLiteral<'db> {
51145088 ///
51155089 /// Returns an empty slice if the bases cannot be computed (e.g., due to a cycle)
51165090 /// or if the bases argument is not a tuple.
5091+ ///
5092+ /// Returns `[Unknown]` if the bases tuple is variable-length (like `tuple[type, ...]`).
51175093 #[ salsa:: tracked( returns( deref) , cycle_initial=dynamic_class_explicit_bases_cycle_initial, heap_size=ruff_memory_usage:: heap_size) ]
51185094 pub ( crate ) fn explicit_bases ( self , db : & ' db dyn Db ) -> Box < [ Type < ' db > ] > {
51195095 let scope = self . scope ( db) ;
@@ -5162,7 +5138,7 @@ impl<'db> DynamicClassLiteral<'db> {
51625138 } ;
51635139
51645140 // For variable-length tuples (like `tuple[type, ...]`), we can't statically
5165- // determine the bases, so return a single Unknown base .
5141+ // determine the bases, so return Unknown.
51665142 let Some ( tuple_spec) = bases_type. tuple_instance_spec ( db) else {
51675143 return Box :: from ( [ Type :: unknown ( ) ] ) ;
51685144 } ;
@@ -5235,12 +5211,12 @@ impl<'db> DynamicClassLiteral<'db> {
52355211 self ,
52365212 db : & ' db dyn Db ,
52375213 ) -> Result < Type < ' db > , DynamicMetaclassConflict < ' db > > {
5238- let base_types = self . explicit_bases ( db) ;
5214+ let original_bases = self . explicit_bases ( db) ;
52395215
52405216 // If no bases, metaclass is `type`.
52415217 // To dynamically create a class with no bases that has a custom metaclass,
52425218 // you have to invoke that metaclass rather than `type()`.
5243- if base_types . is_empty ( ) {
5219+ if original_bases . is_empty ( ) {
52445220 return Ok ( KnownClass :: Type . to_class_literal ( db) ) ;
52455221 }
52465222
@@ -5249,17 +5225,20 @@ impl<'db> DynamicClassLiteral<'db> {
52495225 return Ok ( SubclassOfType :: subclass_of_unknown ( ) ) ;
52505226 }
52515227
5252- // Convert types to ClassBases. Use a placeholder class for conversion .
5228+ // Convert Types to ClassBases for metaclass computation .
52535229 let placeholder_class: ClassLiteral < ' db > =
52545230 KnownClass :: Object . try_to_class_literal ( db) . unwrap ( ) . into ( ) ;
5255- let bases: Vec < ClassBase < ' db > > = base_types
5231+
5232+ let bases: Vec < ClassBase < ' db > > = original_bases
52565233 . iter ( )
5257- . map ( |ty| {
5258- ClassBase :: try_from_type ( db, * ty, placeholder_class)
5259- . unwrap_or_else ( ClassBase :: unknown)
5260- } )
5234+ . filter_map ( |base_type| ClassBase :: try_from_type ( db, * base_type, placeholder_class) )
52615235 . collect ( ) ;
52625236
5237+ // If all bases failed to convert, return type as the metaclass.
5238+ if bases. is_empty ( ) {
5239+ return Ok ( KnownClass :: Type . to_class_literal ( db) ) ;
5240+ }
5241+
52635242 // Start with the first base's metaclass as the candidate.
52645243 let mut candidate = bases[ 0 ] . metaclass ( db) ;
52655244
@@ -8211,18 +8190,27 @@ impl KnownClass {
82118190 if let Err ( error) = dynamic_class. try_mro ( db) {
82128191 match error. reason ( ) {
82138192 DynamicMroErrorKind :: InvalidBases ( invalid_bases) => {
8214- for ( _, invalid_type ) in invalid_bases {
8193+ for ( _, base_type ) in invalid_bases {
82158194 if let Some ( builder) =
82168195 context. report_lint ( & INVALID_BASE , call_expression)
82178196 {
82188197 builder. into_diagnostic ( format_args ! (
8219- "Invalid class base with type `{}` (all bases must be a class, \
8220- `Any`, `Unknown` or `Todo`)",
8221- invalid_type. display( db) ,
8198+ "Invalid class base with type `{}`" ,
8199+ base_type. display( db)
82228200 ) ) ;
82238201 }
82248202 }
82258203 }
8204+ DynamicMroErrorKind :: InheritanceCycle => {
8205+ if let Some ( builder) =
8206+ context. report_lint ( & CYCLIC_CLASS_DEFINITION , call_expression)
8207+ {
8208+ builder. into_diagnostic ( format_args ! (
8209+ "Cyclic definition of `{}`" ,
8210+ dynamic_class. name( db)
8211+ ) ) ;
8212+ }
8213+ }
82268214 DynamicMroErrorKind :: DuplicateBases ( duplicates) => {
82278215 if let Some ( builder) =
82288216 context. report_lint ( & DUPLICATE_BASE , call_expression)
0 commit comments