@@ -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 ! {
@@ -6506,10 +6522,74 @@ impl<'db> Type<'db> {
65066522 }
65076523
65086524 pub ( super ) fn has_divergent_type ( self , db : & ' db dyn Db , div : Type < ' db > ) -> bool {
6509- any_over_type ( db, self , & |ty| match ty {
6510- Type :: Dynamic ( DynamicType :: Divergent ( _) ) => ty == div,
6511- _ => false ,
6512- } )
6525+ self . has_divergent_type_impl ( db, div, & HasDivergentTypeVisitor :: new ( false ) )
6526+ }
6527+
6528+ pub ( super ) fn has_divergent_type_impl (
6529+ self ,
6530+ db : & ' db dyn Db ,
6531+ div : Type < ' db > ,
6532+ visitor : & HasDivergentTypeVisitor < ' db > ,
6533+ ) -> bool {
6534+ // We don't use `any_over_type` here because we don't need/want to descend into lazy parts
6535+ // of types (typevar bounds/constraints, type alias values, etc) here.
6536+ match self {
6537+ Type :: Dynamic ( DynamicType :: Divergent ( _) ) => self == div,
6538+ Type :: Union ( union) => union. has_divergent_type_impl ( db, div, visitor) ,
6539+ Type :: Intersection ( intersection) => {
6540+ intersection. has_divergent_type_impl ( db, div, visitor)
6541+ }
6542+ Type :: GenericAlias ( alias) => visitor. visit ( self , || {
6543+ alias
6544+ . specialization ( db)
6545+ . has_divergent_type_impl ( db, div, visitor)
6546+ } ) ,
6547+ Type :: NominalInstance ( instance) => visitor. visit ( self , || {
6548+ instance. class ( db) . has_divergent_type_impl ( db, div, visitor)
6549+ } ) ,
6550+ Type :: Callable ( callable) => {
6551+ visitor. visit ( self , || callable. has_divergent_type_impl ( db, div, visitor) )
6552+ }
6553+ Type :: ProtocolInstance ( protocol) => {
6554+ visitor. visit ( self , || protocol. has_divergent_type_impl ( db, div, visitor) )
6555+ }
6556+ Type :: PropertyInstance ( property) => property. has_divergent_type_impl ( db, div, visitor) ,
6557+ Type :: TypeIs ( type_is) => type_is
6558+ . return_type ( db)
6559+ . has_divergent_type_impl ( db, div, visitor) ,
6560+ Type :: SubclassOf ( subclass_of) => visitor. visit ( self , || {
6561+ subclass_of. has_divergent_type_impl ( db, div, visitor)
6562+ } ) ,
6563+ Type :: TypedDict ( typed_dict) => visitor. visit ( self , || {
6564+ typed_dict
6565+ . defining_class ( )
6566+ . has_divergent_type_impl ( db, div, visitor)
6567+ } ) ,
6568+ Type :: Never
6569+ | Type :: AlwaysTruthy
6570+ | Type :: AlwaysFalsy
6571+ | Type :: WrapperDescriptor ( _)
6572+ | Type :: DataclassDecorator ( _)
6573+ | Type :: DataclassTransformer ( _)
6574+ | Type :: ModuleLiteral ( _)
6575+ | Type :: ClassLiteral ( _)
6576+ | Type :: IntLiteral ( _)
6577+ | Type :: BooleanLiteral ( _)
6578+ | Type :: LiteralString
6579+ | Type :: StringLiteral ( _)
6580+ | Type :: BytesLiteral ( _)
6581+ | Type :: EnumLiteral ( _)
6582+ | Type :: BoundSuper ( _)
6583+ | Type :: SpecialForm ( _)
6584+ | Type :: KnownInstance ( _)
6585+ | Type :: NonInferableTypeVar ( _)
6586+ | Type :: TypeVar ( _)
6587+ | Type :: FunctionLiteral ( _)
6588+ | Type :: KnownBoundMethod ( _)
6589+ | Type :: BoundMethod ( _)
6590+ | Type :: Dynamic ( _)
6591+ | Type :: TypeAlias ( _) => false ,
6592+ }
65136593 }
65146594}
65156595
@@ -9155,6 +9235,16 @@ impl<'db> CallableType<'db> {
91559235 . is_equivalent_to_impl ( db, other. signatures ( db) , visitor)
91569236 } )
91579237 }
9238+
9239+ fn has_divergent_type_impl (
9240+ self ,
9241+ db : & ' db dyn Db ,
9242+ div : Type < ' db > ,
9243+ visitor : & HasDivergentTypeVisitor < ' db > ,
9244+ ) -> bool {
9245+ self . signatures ( db)
9246+ . has_divergent_type_impl ( db, div, visitor)
9247+ }
91589248}
91599249
91609250/// Represents a specific instance of a bound method type for a builtin class.
@@ -10129,6 +10219,17 @@ impl<'db> UnionType<'db> {
1012910219
1013010220 ConstraintSet :: from ( sorted_self == other. normalized ( db) )
1013110221 }
10222+
10223+ fn has_divergent_type_impl (
10224+ self ,
10225+ db : & ' db dyn Db ,
10226+ div : Type < ' db > ,
10227+ visitor : & HasDivergentTypeVisitor < ' db > ,
10228+ ) -> bool {
10229+ self . elements ( db)
10230+ . iter ( )
10231+ . any ( |ty| ty. has_divergent_type_impl ( db, div, visitor) )
10232+ }
1013210233}
1013310234
1013410235#[ salsa:: interned( debug, heap_size=IntersectionType :: heap_size) ]
@@ -10344,6 +10445,21 @@ impl<'db> IntersectionType<'db> {
1034410445 ruff_memory_usage:: order_set_heap_size ( positive)
1034510446 + ruff_memory_usage:: order_set_heap_size ( negative)
1034610447 }
10448+
10449+ fn has_divergent_type_impl (
10450+ self ,
10451+ db : & ' db dyn Db ,
10452+ div : Type < ' db > ,
10453+ visitor : & HasDivergentTypeVisitor < ' db > ,
10454+ ) -> bool {
10455+ self . positive ( db)
10456+ . iter ( )
10457+ . any ( |ty| ty. has_divergent_type_impl ( db, div, visitor) )
10458+ || self
10459+ . negative ( db)
10460+ . iter ( )
10461+ . any ( |ty| ty. has_divergent_type_impl ( db, div, visitor) )
10462+ }
1034710463}
1034810464
1034910465/// # Ordering
0 commit comments