@@ -3,10 +3,7 @@ use std::collections::BTreeSet;
33use crate :: Db ;
44use crate :: semantic_index:: definition:: { Definition , DefinitionKind } ;
55use crate :: types:: constraints:: ConstraintSet ;
6- use crate :: types:: {
7- ApplyTypeMappingVisitor , ClassType , NormalizedVisitor , Type , TypeContext , TypeMapping ,
8- definition_expression_type,
9- } ;
6+ use crate :: types:: { ClassType , Type , definition_expression_type} ;
107use ruff_db:: parsed:: parsed_module;
118use ruff_python_ast as ast;
129
@@ -133,7 +130,10 @@ impl<'db> NewType<'db> {
133130 ConstraintSet :: from ( !self . is_subtype_of ( db, other) && !other. is_subtype_of ( db, self ) )
134131 }
135132
136- fn map_base_class_type (
133+ /// Create a new `NewType` by mapping the underlying `ClassType`. This descends through any
134+ /// number of nested `NewType` layers and rebuilds the whole chain. In the rare case of cyclic
135+ /// `NewType`s with no underlying `ClassType`, this has no effect and does not call `f`.
136+ pub ( crate ) fn map_base_class_type (
137137 self ,
138138 db : & ' db dyn Db ,
139139 f : impl FnOnce ( ClassType < ' db > ) -> ClassType < ' db > ,
@@ -149,10 +149,11 @@ impl<'db> NewType<'db> {
149149 for base in self . iter_bases ( db) {
150150 match base {
151151 // Build up the stack of intermediate newtypes that we'll need to re-wrap after
152- // we've mapped the base class .
152+ // we've mapped the `ClassType` .
153153 NewTypeBase :: NewType ( base_newtype) => inner_newtype_stack. push ( base_newtype) ,
154+ // We've reached the `ClassType`.
154155 NewTypeBase :: ClassType ( base_class_type) => {
155- // We've reached the base class. Call `f`.
156+ // Call `f`.
156157 let mut mapped_base = NewTypeBase :: ClassType ( f ( base_class_type) ) ;
157158 // Re-wrap the mapped base class in however many newtypes we unwrapped.
158159 for inner_newtype in inner_newtype_stack. into_iter ( ) . rev ( ) {
@@ -172,74 +173,10 @@ impl<'db> NewType<'db> {
172173 }
173174 }
174175 }
175- // If we get here, there is no base class (because this newtype is cyclic), and we don't
176+ // If we get here, there is no `ClassType` (because this newtype is cyclic), and we don't
176177 // call `f` at all.
177178 self
178179 }
179-
180- pub ( crate ) fn normalized_impl ( self , db : & ' db dyn Db , visitor : & NormalizedVisitor < ' db > ) -> Self {
181- self . map_base_class_type ( db, |base_class_type| {
182- // We don't really want to work with `Type` directly (`NewTypeBase` is much more
183- // restricted), but we need to let the `NormalizedVisitor` see the `Type` of the base
184- // class, because this is its only chance to detect an indirect cycle like this one:
185- //
186- // ```py
187- // Foo = NewType("Foo", list["Foo"])
188- // ```
189- //
190- // Note that NewTypeBaseIter does *not* catch this kind of cycle, because it stops
191- // after returning `NewTypeBase::ClassType` for `list`.
192- let mut is_first_encounter = false ;
193- let base_type = Type :: instance ( db, base_class_type) ;
194- _ = visitor. visit ( base_type, || {
195- // If we hit in a cycle, this closure won't run.
196- is_first_encounter = true ;
197- base_type
198- } ) ;
199- if is_first_encounter {
200- base_class_type. normalized_impl ( db, visitor)
201- } else {
202- // Cycle detected. Fall back to `object`.
203- // REVIEWERS: Is this correct?
204- ClassType :: object ( db)
205- }
206- } )
207- }
208-
209- pub ( crate ) fn apply_type_mapping_impl < ' a > (
210- self ,
211- db : & ' db dyn Db ,
212- type_mapping : & TypeMapping < ' a , ' db > ,
213- tcx : TypeContext < ' db > ,
214- visitor : & ApplyTypeMappingVisitor < ' db > ,
215- ) -> Self {
216- self . map_base_class_type ( db, |base_class_type| {
217- // We don't really want to work with `Type` directly (`NewTypeBase` is much more
218- // restricted), but we need to let the `ApplyTypeMappingVisitor` see the `Type` of the
219- // base class, because this is its only chance to detect an indirect cycle like:
220- //
221- // ```py
222- // Foo = NewType("Foo", list["Foo"])
223- // ```
224- //
225- // Note that NewTypeBaseIter does *not* catch this kind of cycle, because it stops
226- // after returning `NewTypeBase::ClassType` for `list`.
227- let mut is_first_encounter = false ;
228- let base_type = Type :: instance ( db, base_class_type) ;
229- _ = visitor. visit ( base_type, || {
230- // If we hit in a cycle, this closure won't run.
231- is_first_encounter = true ;
232- base_type
233- } ) ;
234- if is_first_encounter {
235- base_class_type. apply_type_mapping_impl ( db, type_mapping, tcx, visitor)
236- } else {
237- // Cycle detected. Fall back to `object`.
238- // REVIEWERS: Is this correct?
239- ClassType :: object ( db)
240- }
241- } )
242- }
243180}
244181
245182/// `typing.NewType` typically wraps a class type, but it can also wrap another newtype.
0 commit comments