@@ -65,7 +65,6 @@ use crate::types::signatures::{Parameter, ParameterForm, Parameters, walk_signat
6565use crate :: types:: tuple:: TupleSpec ;
6666pub ( crate ) use crate :: types:: typed_dict:: { TypedDictParams , TypedDictType , walk_typed_dict_type} ;
6767use crate :: types:: variance:: { TypeVarVariance , VarianceInferable } ;
68- use crate :: types:: visitor:: any_over_type;
6968use crate :: unpack:: EvaluationMode ;
7069pub use crate :: util:: diagnostics:: add_inferred_python_version_hint_to_diagnostic;
7170use crate :: { Db , FxOrderSet , Module , Program } ;
@@ -222,6 +221,10 @@ pub(crate) struct TryBool;
222221pub ( crate ) type NormalizedVisitor < ' db > = TypeTransformer < ' db , Normalized > ;
223222pub ( crate ) struct Normalized ;
224223
224+ /// A [`CycleDetector`] that is used in `has_divergent_type` methods.
225+ pub ( crate ) type HasDivergentTypeVisitor < ' db > = CycleDetector < HasDivergentType , Type < ' db > , bool > ;
226+ pub ( crate ) struct HasDivergentType ;
227+
225228/// How a generic type has been specialized.
226229///
227230/// This matters only if there is at least one invariant type parameter.
@@ -588,6 +591,19 @@ impl<'db> PropertyInstanceType<'db> {
588591
589592 getter_equivalence. and ( db, setter_equivalence)
590593 }
594+
595+ fn has_divergent_type_impl (
596+ self ,
597+ db : & ' db dyn Db ,
598+ div : Type < ' db > ,
599+ visitor : & HasDivergentTypeVisitor < ' db > ,
600+ ) -> bool {
601+ self . getter ( db)
602+ . is_some_and ( |ty| ty. has_divergent_type_impl ( db, div, visitor) )
603+ || self
604+ . setter ( db)
605+ . is_some_and ( |ty| ty. has_divergent_type_impl ( db, div, visitor) )
606+ }
591607}
592608
593609bitflags ! {
@@ -6516,10 +6532,74 @@ impl<'db> Type<'db> {
65166532 }
65176533
65186534 pub ( super ) fn has_divergent_type ( self , db : & ' db dyn Db , div : Type < ' db > ) -> bool {
6519- any_over_type ( db, self , & |ty| match ty {
6520- Type :: Dynamic ( DynamicType :: Divergent ( _) ) => ty == div,
6521- _ => false ,
6522- } )
6535+ self . has_divergent_type_impl ( db, div, & HasDivergentTypeVisitor :: new ( false ) )
6536+ }
6537+
6538+ pub ( super ) fn has_divergent_type_impl (
6539+ self ,
6540+ db : & ' db dyn Db ,
6541+ div : Type < ' db > ,
6542+ visitor : & HasDivergentTypeVisitor < ' db > ,
6543+ ) -> bool {
6544+ // We don't use `any_over_type` here because we don't need/want to descend into lazy parts
6545+ // of types (typevar bounds/constraints, type alias values, etc) here.
6546+ match self {
6547+ Type :: Dynamic ( DynamicType :: Divergent ( _) ) => self == div,
6548+ Type :: Union ( union) => union. has_divergent_type_impl ( db, div, visitor) ,
6549+ Type :: Intersection ( intersection) => {
6550+ intersection. has_divergent_type_impl ( db, div, visitor)
6551+ }
6552+ Type :: GenericAlias ( alias) => visitor. visit ( self , || {
6553+ alias
6554+ . specialization ( db)
6555+ . has_divergent_type_impl ( db, div, visitor)
6556+ } ) ,
6557+ Type :: NominalInstance ( instance) => visitor. visit ( self , || {
6558+ instance. class ( db) . has_divergent_type_impl ( db, div, visitor)
6559+ } ) ,
6560+ Type :: Callable ( callable) => {
6561+ visitor. visit ( self , || callable. has_divergent_type_impl ( db, div, visitor) )
6562+ }
6563+ Type :: ProtocolInstance ( protocol) => {
6564+ visitor. visit ( self , || protocol. has_divergent_type_impl ( db, div, visitor) )
6565+ }
6566+ Type :: PropertyInstance ( property) => property. has_divergent_type_impl ( db, div, visitor) ,
6567+ Type :: TypeIs ( type_is) => type_is
6568+ . return_type ( db)
6569+ . has_divergent_type_impl ( db, div, visitor) ,
6570+ Type :: SubclassOf ( subclass_of) => visitor. visit ( self , || {
6571+ subclass_of. has_divergent_type_impl ( db, div, visitor)
6572+ } ) ,
6573+ Type :: TypedDict ( typed_dict) => visitor. visit ( self , || {
6574+ typed_dict
6575+ . defining_class ( )
6576+ . has_divergent_type_impl ( db, div, visitor)
6577+ } ) ,
6578+ Type :: Never
6579+ | Type :: AlwaysTruthy
6580+ | Type :: AlwaysFalsy
6581+ | Type :: WrapperDescriptor ( _)
6582+ | Type :: DataclassDecorator ( _)
6583+ | Type :: DataclassTransformer ( _)
6584+ | Type :: ModuleLiteral ( _)
6585+ | Type :: ClassLiteral ( _)
6586+ | Type :: IntLiteral ( _)
6587+ | Type :: BooleanLiteral ( _)
6588+ | Type :: LiteralString
6589+ | Type :: StringLiteral ( _)
6590+ | Type :: BytesLiteral ( _)
6591+ | Type :: EnumLiteral ( _)
6592+ | Type :: BoundSuper ( _)
6593+ | Type :: SpecialForm ( _)
6594+ | Type :: KnownInstance ( _)
6595+ | Type :: NonInferableTypeVar ( _)
6596+ | Type :: TypeVar ( _)
6597+ | Type :: FunctionLiteral ( _)
6598+ | Type :: KnownBoundMethod ( _)
6599+ | Type :: BoundMethod ( _)
6600+ | Type :: Dynamic ( _)
6601+ | Type :: TypeAlias ( _) => false ,
6602+ }
65236603 }
65246604}
65256605
@@ -9181,6 +9261,16 @@ impl<'db> CallableType<'db> {
91819261 . is_equivalent_to_impl ( db, other. signatures ( db) , visitor)
91829262 } )
91839263 }
9264+
9265+ fn has_divergent_type_impl (
9266+ self ,
9267+ db : & ' db dyn Db ,
9268+ div : Type < ' db > ,
9269+ visitor : & HasDivergentTypeVisitor < ' db > ,
9270+ ) -> bool {
9271+ self . signatures ( db)
9272+ . has_divergent_type_impl ( db, div, visitor)
9273+ }
91849274}
91859275
91869276/// Represents a specific instance of a bound method type for a builtin class.
@@ -10155,6 +10245,17 @@ impl<'db> UnionType<'db> {
1015510245
1015610246 ConstraintSet :: from ( sorted_self == other. normalized ( db) )
1015710247 }
10248+
10249+ fn has_divergent_type_impl (
10250+ self ,
10251+ db : & ' db dyn Db ,
10252+ div : Type < ' db > ,
10253+ visitor : & HasDivergentTypeVisitor < ' db > ,
10254+ ) -> bool {
10255+ self . elements ( db)
10256+ . iter ( )
10257+ . any ( |ty| ty. has_divergent_type_impl ( db, div, visitor) )
10258+ }
1015810259}
1015910260
1016010261#[ salsa:: interned( debug, heap_size=IntersectionType :: heap_size) ]
@@ -10370,6 +10471,21 @@ impl<'db> IntersectionType<'db> {
1037010471 ruff_memory_usage:: order_set_heap_size ( positive)
1037110472 + ruff_memory_usage:: order_set_heap_size ( negative)
1037210473 }
10474+
10475+ fn has_divergent_type_impl (
10476+ self ,
10477+ db : & ' db dyn Db ,
10478+ div : Type < ' db > ,
10479+ visitor : & HasDivergentTypeVisitor < ' db > ,
10480+ ) -> bool {
10481+ self . positive ( db)
10482+ . iter ( )
10483+ . any ( |ty| ty. has_divergent_type_impl ( db, div, visitor) )
10484+ || self
10485+ . negative ( db)
10486+ . iter ( )
10487+ . any ( |ty| ty. has_divergent_type_impl ( db, div, visitor) )
10488+ }
1037310489}
1037410490
1037510491/// # Ordering
0 commit comments