From 4dc88a0a5f58f92c94584ddd1bc6c5ca694c20f9 Mon Sep 17 00:00:00 2001 From: David Peter Date: Tue, 14 Oct 2025 16:46:45 +0200 Subject: [PATCH] [ty] Store `field_specifiers` --- crates/ty_python_semantic/src/types.rs | 70 ++++++++++------ .../ty_python_semantic/src/types/call/bind.rs | 82 ++++++++++--------- crates/ty_python_semantic/src/types/class.rs | 77 ++++++++--------- .../ty_python_semantic/src/types/function.rs | 24 ++++-- .../src/types/infer/builder.rs | 11 ++- .../src/types/type_ordering.rs | 8 +- 6 files changed, 155 insertions(+), 117 deletions(-) diff --git a/crates/ty_python_semantic/src/types.rs b/crates/ty_python_semantic/src/types.rs index 3f8702c858..d6dba6f194 100644 --- a/crates/ty_python_semantic/src/types.rs +++ b/crates/ty_python_semantic/src/types.rs @@ -32,7 +32,7 @@ pub(crate) use self::signatures::{CallableSignature, Parameter, Parameters, Sign pub(crate) use self::subclass_of::{SubclassOfInner, SubclassOfType}; use crate::module_name::ModuleName; use crate::module_resolver::{KnownModule, resolve_module}; -use crate::place::{Boundness, Place, PlaceAndQualifiers, imported_symbol}; +use crate::place::{Boundness, Place, PlaceAndQualifiers, imported_symbol, known_module_symbol}; use crate::semantic_index::definition::{Definition, DefinitionKind}; use crate::semantic_index::place::ScopedPlaceId; use crate::semantic_index::scope::ScopeId; @@ -50,7 +50,8 @@ pub use crate::types::display::DisplaySettings; use crate::types::display::TupleSpecialization; use crate::types::enums::{enum_metadata, is_single_member_enum}; use crate::types::function::{ - DataclassTransformerParams, FunctionSpans, FunctionType, KnownFunction, + DataclassTransformerFlags, DataclassTransformerParams, FunctionSpans, FunctionType, + KnownFunction, }; use crate::types::generics::{ GenericContext, InferableTypeVars, PartialSpecialization, Specialization, bind_typevar, @@ -622,8 +623,8 @@ bitflags! { /// that were passed in. For the precise meaning of the fields, see [1]. /// /// [1]: https://docs.python.org/3/library/dataclasses.html - #[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)] - pub struct DataclassParams: u16 { + #[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, PartialOrd, Ord)] + pub struct DataclassFlags: u16 { const INIT = 0b0000_0000_0001; const REPR = 0b0000_0000_0010; const EQ = 0b0000_0000_0100; @@ -634,51 +635,74 @@ bitflags! { const KW_ONLY = 0b0000_1000_0000; const SLOTS = 0b0001_0000_0000; const WEAKREF_SLOT = 0b0010_0000_0000; - // This is not an actual argument from `dataclass(...)` but a flag signaling that no - // `field_specifiers` was specified for the `dataclass_transform`, see [1]. - // [1]: https://typing.python.org/en/latest/spec/dataclasses.html#dataclass-transform-parameters - const NO_FIELD_SPECIFIERS = 0b0100_0000_0000; } } -impl get_size2::GetSize for DataclassParams {} +impl get_size2::GetSize for DataclassFlags {} -impl Default for DataclassParams { +impl Default for DataclassFlags { fn default() -> Self { Self::INIT | Self::REPR | Self::EQ | Self::MATCH_ARGS } } -impl From for DataclassParams { - fn from(params: DataclassTransformerParams) -> Self { +impl From for DataclassFlags { + fn from(params: DataclassTransformerFlags) -> Self { let mut result = Self::default(); result.set( Self::EQ, - params.contains(DataclassTransformerParams::EQ_DEFAULT), + params.contains(DataclassTransformerFlags::EQ_DEFAULT), ); result.set( Self::ORDER, - params.contains(DataclassTransformerParams::ORDER_DEFAULT), + params.contains(DataclassTransformerFlags::ORDER_DEFAULT), ); result.set( Self::KW_ONLY, - params.contains(DataclassTransformerParams::KW_ONLY_DEFAULT), + params.contains(DataclassTransformerFlags::KW_ONLY_DEFAULT), ); result.set( Self::FROZEN, - params.contains(DataclassTransformerParams::FROZEN_DEFAULT), - ); - - result.set( - Self::NO_FIELD_SPECIFIERS, - !params.contains(DataclassTransformerParams::FIELD_SPECIFIERS), + params.contains(DataclassTransformerFlags::FROZEN_DEFAULT), ); result } } +#[salsa::interned(debug, heap_size=ruff_memory_usage::heap_size)] +#[derive(PartialOrd, Ord)] +pub struct DataclassParams<'db> { + flags: DataclassFlags, + field_specifiers: Type<'db>, +} + +impl get_size2::GetSize for DataclassParams<'_> {} + +impl<'db> DataclassParams<'db> { + fn default_params(db: &'db dyn Db) -> Self { + Self::from_flags(db, DataclassFlags::default()) + } + + fn from_flags(db: &'db dyn Db, flags: DataclassFlags) -> Self { + let dataclasses_field = known_module_symbol(db, KnownModule::Dataclasses, "field") + .place + .ignore_possibly_unbound() + .unwrap_or_else(|| Type::none(db)); + + Self::new(db, flags, dataclasses_field) + } + + fn from_transformer_params(db: &'db dyn Db, params: DataclassTransformerParams<'db>) -> Self { + Self::new( + db, + DataclassFlags::from(params.flags(db)), + params.field_specifiers(db), + ) + } +} + /// Representation of a type: a set of possible values at runtime. /// #[derive(Copy, Clone, Debug, PartialEq, Eq, Hash, salsa::Update, get_size2::GetSize)] @@ -719,9 +743,9 @@ pub enum Type<'db> { /// A special callable that is returned by a `dataclass(…)` call. It is usually /// used as a decorator. Note that this is only used as a return type for actual /// `dataclass` calls, not for the argumentless `@dataclass` decorator. - DataclassDecorator(DataclassParams), + DataclassDecorator(DataclassParams<'db>), /// A special callable that is returned by a `dataclass_transform(…)` call. - DataclassTransformer(DataclassTransformerParams), + DataclassTransformer(DataclassTransformerParams<'db>), /// The type of an arbitrary callable object with a certain specified signature. Callable(CallableType<'db>), /// A specific module object diff --git a/crates/ty_python_semantic/src/types/call/bind.rs b/crates/ty_python_semantic/src/types/call/bind.rs index 41a392a88b..b78c776a43 100644 --- a/crates/ty_python_semantic/src/types/call/bind.rs +++ b/crates/ty_python_semantic/src/types/call/bind.rs @@ -24,7 +24,8 @@ use crate::types::diagnostic::{ }; use crate::types::enums::is_enum_class; use crate::types::function::{ - DataclassTransformerParams, FunctionDecorators, FunctionType, KnownFunction, OverloadLiteral, + DataclassTransformerFlags, DataclassTransformerParams, FunctionDecorators, FunctionType, + KnownFunction, OverloadLiteral, }; use crate::types::generics::{ InferableTypeVars, Specialization, SpecializationBuilder, SpecializationError, @@ -32,9 +33,9 @@ use crate::types::generics::{ use crate::types::signatures::{Parameter, ParameterForm, ParameterKind, Parameters}; use crate::types::tuple::{TupleLength, TupleType}; use crate::types::{ - BoundMethodType, ClassLiteral, DataclassParams, FieldInstance, KnownBoundMethodType, - KnownClass, KnownInstanceType, MemberLookupPolicy, PropertyInstanceType, SpecialFormType, - TrackedConstraintSet, TypeAliasType, TypeContext, UnionBuilder, UnionType, + BoundMethodType, ClassLiteral, DataclassFlags, DataclassParams, FieldInstance, + KnownBoundMethodType, KnownClass, KnownInstanceType, MemberLookupPolicy, PropertyInstanceType, + SpecialFormType, TrackedConstraintSet, TypeAliasType, TypeContext, UnionBuilder, UnionType, WrapperDescriptorKind, enums, ide_support, infer_isolated_expression, todo_type, }; use ruff_db::diagnostic::{Annotation, Diagnostic, SubDiagnostic, SubDiagnosticSeverity}; @@ -871,43 +872,45 @@ impl<'db> Bindings<'db> { weakref_slot, ] = overload.parameter_types() { - let mut params = DataclassParams::empty(); + let mut flags = DataclassFlags::empty(); if to_bool(init, true) { - params |= DataclassParams::INIT; + flags |= DataclassFlags::INIT; } if to_bool(repr, true) { - params |= DataclassParams::REPR; + flags |= DataclassFlags::REPR; } if to_bool(eq, true) { - params |= DataclassParams::EQ; + flags |= DataclassFlags::EQ; } if to_bool(order, false) { - params |= DataclassParams::ORDER; + flags |= DataclassFlags::ORDER; } if to_bool(unsafe_hash, false) { - params |= DataclassParams::UNSAFE_HASH; + flags |= DataclassFlags::UNSAFE_HASH; } if to_bool(frozen, false) { - params |= DataclassParams::FROZEN; + flags |= DataclassFlags::FROZEN; } if to_bool(match_args, true) { - params |= DataclassParams::MATCH_ARGS; + flags |= DataclassFlags::MATCH_ARGS; } if to_bool(kw_only, false) { if Program::get(db).python_version(db) >= PythonVersion::PY310 { - params |= DataclassParams::KW_ONLY; + flags |= DataclassFlags::KW_ONLY; } else { // TODO: emit diagnostic } } if to_bool(slots, false) { - params |= DataclassParams::SLOTS; + flags |= DataclassFlags::SLOTS; } if to_bool(weakref_slot, false) { - params |= DataclassParams::WEAKREF_SLOT; + flags |= DataclassFlags::WEAKREF_SLOT; } + let params = DataclassParams::from_flags(db, flags); + overload.set_return_type(Type::DataclassDecorator(params)); } @@ -915,7 +918,7 @@ impl<'db> Bindings<'db> { if let [Some(Type::ClassLiteral(class_literal))] = overload.parameter_types() { - let params = DataclassParams::default(); + let params = DataclassParams::default_params(db); overload.set_return_type(Type::from(ClassLiteral::new( db, class_literal.name(db), @@ -938,30 +941,26 @@ impl<'db> Bindings<'db> { _kwargs, ] = overload.parameter_types() { - let mut params = DataclassTransformerParams::empty(); + let mut flags = DataclassTransformerFlags::empty(); if to_bool(eq_default, true) { - params |= DataclassTransformerParams::EQ_DEFAULT; + flags |= DataclassTransformerFlags::EQ_DEFAULT; } if to_bool(order_default, false) { - params |= DataclassTransformerParams::ORDER_DEFAULT; + flags |= DataclassTransformerFlags::ORDER_DEFAULT; } if to_bool(kw_only_default, false) { - params |= DataclassTransformerParams::KW_ONLY_DEFAULT; + flags |= DataclassTransformerFlags::KW_ONLY_DEFAULT; } if to_bool(frozen_default, false) { - params |= DataclassTransformerParams::FROZEN_DEFAULT; + flags |= DataclassTransformerFlags::FROZEN_DEFAULT; } - if let Some(field_specifiers_type) = field_specifiers { - // For now, we'll do a simple check: if field_specifiers is not - // None/empty, we assume it might contain dataclasses.field - // TODO: Implement proper parsing to check for - // dataclasses.field/Field specifically - if !field_specifiers_type.is_none(db) { - params |= DataclassTransformerParams::FIELD_SPECIFIERS; - } - } + let params = DataclassTransformerParams::new( + db, + flags, + field_specifiers.unwrap_or(Type::none(db)), + ); overload.set_return_type(Type::DataclassTransformer(params)); } @@ -1026,36 +1025,41 @@ impl<'db> Bindings<'db> { // the argument type and overwrite the corresponding flag in `dataclass_params` after // constructing them from the `dataclass_transformer`-parameter defaults. - let mut dataclass_params = - DataclassParams::from(params); + let dataclass_params = + DataclassParams::from_transformer_params( + db, params, + ); + let mut flags = dataclass_params.flags(db); if let Ok(Some(Type::BooleanLiteral(order))) = overload.parameter_type_by_name("order") { - dataclass_params.set(DataclassParams::ORDER, order); + flags.set(DataclassFlags::ORDER, order); } if let Ok(Some(Type::BooleanLiteral(eq))) = overload.parameter_type_by_name("eq") { - dataclass_params.set(DataclassParams::EQ, eq); + flags.set(DataclassFlags::EQ, eq); } if let Ok(Some(Type::BooleanLiteral(kw_only))) = overload.parameter_type_by_name("kw_only") { - dataclass_params - .set(DataclassParams::KW_ONLY, kw_only); + flags.set(DataclassFlags::KW_ONLY, kw_only); } if let Ok(Some(Type::BooleanLiteral(frozen))) = overload.parameter_type_by_name("frozen") { - dataclass_params - .set(DataclassParams::FROZEN, frozen); + flags.set(DataclassFlags::FROZEN, frozen); } - Type::DataclassDecorator(dataclass_params) + Type::DataclassDecorator(DataclassParams::new( + db, + flags, + dataclass_params.field_specifiers(db), + )) }, ) }) diff --git a/crates/ty_python_semantic/src/types/class.rs b/crates/ty_python_semantic/src/types/class.rs index 876f6d3930..be9f311616 100644 --- a/crates/ty_python_semantic/src/types/class.rs +++ b/crates/ty_python_semantic/src/types/class.rs @@ -31,12 +31,12 @@ use crate::types::tuple::{TupleSpec, TupleType}; use crate::types::typed_dict::typed_dict_params_from_class_def; use crate::types::visitor::{NonAtomicType, TypeKind, TypeVisitor, walk_non_atomic_type}; use crate::types::{ - ApplyTypeMappingVisitor, Binding, BoundSuperType, CallableType, DataclassParams, - DeprecatedInstance, FindLegacyTypeVarsVisitor, HasRelationToVisitor, IsDisjointVisitor, - IsEquivalentVisitor, KnownInstanceType, ManualPEP695TypeAliasType, MaterializationKind, - NormalizedVisitor, PropertyInstanceType, StringLiteralType, TypeAliasType, TypeContext, - TypeMapping, TypeRelation, TypedDictParams, UnionBuilder, VarianceInferable, declaration_type, - determine_upper_bound, infer_definition_types, + ApplyTypeMappingVisitor, Binding, BoundSuperType, CallableType, DataclassFlags, + DataclassParams, DeprecatedInstance, FindLegacyTypeVarsVisitor, HasRelationToVisitor, + IsDisjointVisitor, IsEquivalentVisitor, KnownInstanceType, ManualPEP695TypeAliasType, + MaterializationKind, NormalizedVisitor, PropertyInstanceType, StringLiteralType, TypeAliasType, + TypeContext, TypeMapping, TypeRelation, TypedDictParams, UnionBuilder, VarianceInferable, + declaration_type, determine_upper_bound, infer_definition_types, }; use crate::{ Db, FxIndexMap, FxIndexSet, FxOrderSet, Program, @@ -162,7 +162,7 @@ fn try_metaclass_cycle_recover<'db>( _count: u32, _self: ClassLiteral<'db>, ) -> salsa::CycleRecoveryAction< - Result<(Type<'db>, Option), MetaclassError<'db>>, + Result<(Type<'db>, Option>), MetaclassError<'db>>, > { salsa::CycleRecoveryAction::Iterate } @@ -171,7 +171,7 @@ fn try_metaclass_cycle_recover<'db>( fn try_metaclass_cycle_initial<'db>( _db: &'db dyn Db, _self_: ClassLiteral<'db>, -) -> Result<(Type<'db>, Option), MetaclassError<'db>> { +) -> Result<(Type<'db>, Option>), MetaclassError<'db>> { Err(MetaclassError { kind: MetaclassErrorKind::Cycle, }) @@ -179,17 +179,17 @@ fn try_metaclass_cycle_initial<'db>( /// A category of classes with code generation capabilities (with synthesized methods). #[derive(Clone, Copy, Debug, PartialEq, salsa::Update, get_size2::GetSize)] -pub(crate) enum CodeGeneratorKind { +pub(crate) enum CodeGeneratorKind<'db> { /// Classes decorated with `@dataclass` or similar dataclass-like decorators - DataclassLike(Option), + DataclassLike(Option>), /// Classes inheriting from `typing.NamedTuple` NamedTuple, /// Classes inheriting from `typing.TypedDict` TypedDict, } -impl CodeGeneratorKind { - pub(crate) fn from_class(db: &dyn Db, class: ClassLiteral<'_>) -> Option { +impl<'db> CodeGeneratorKind<'db> { + pub(crate) fn from_class(db: &'db dyn Db, class: ClassLiteral<'db>) -> Option { #[salsa::tracked( cycle_fn=code_generator_of_class_recover, cycle_initial=code_generator_of_class_initial, @@ -198,7 +198,7 @@ impl CodeGeneratorKind { fn code_generator_of_class<'db>( db: &'db dyn Db, class: ClassLiteral<'db>, - ) -> Option { + ) -> Option> { if class.dataclass_params(db).is_some() { Some(CodeGeneratorKind::DataclassLike(None)) } else if let Ok((_, Some(transformer_params))) = class.try_metaclass(db) { @@ -215,27 +215,27 @@ impl CodeGeneratorKind { } } - fn code_generator_of_class_initial( - _db: &dyn Db, - _class: ClassLiteral<'_>, - ) -> Option { + fn code_generator_of_class_initial<'db>( + _db: &'db dyn Db, + _class: ClassLiteral<'db>, + ) -> Option> { None } - #[expect(clippy::ref_option, clippy::trivially_copy_pass_by_ref)] - fn code_generator_of_class_recover( - _db: &dyn Db, - _value: &Option, + #[expect(clippy::ref_option)] + fn code_generator_of_class_recover<'db>( + _db: &'db dyn Db, + _value: &Option>, _count: u32, - _class: ClassLiteral<'_>, - ) -> salsa::CycleRecoveryAction> { + _class: ClassLiteral<'db>, + ) -> salsa::CycleRecoveryAction>> { salsa::CycleRecoveryAction::Iterate } code_generator_of_class(db, class) } - pub(super) fn matches(self, db: &dyn Db, class: ClassLiteral<'_>) -> bool { + pub(super) fn matches(self, db: &'db dyn Db, class: ClassLiteral<'db>) -> bool { matches!( (CodeGeneratorKind::from_class(db, class), self), (Some(Self::DataclassLike(_)), Self::DataclassLike(_)) @@ -1384,8 +1384,8 @@ pub struct ClassLiteral<'db> { /// If this class is deprecated, this holds the deprecation message. pub(crate) deprecated: Option>, - pub(crate) dataclass_params: Option, - pub(crate) dataclass_transformer_params: Option, + pub(crate) dataclass_params: Option>, + pub(crate) dataclass_transformer_params: Option>, } // The Salsa heap is tracked separately. @@ -1906,7 +1906,7 @@ impl<'db> ClassLiteral<'db> { pub(super) fn try_metaclass( self, db: &'db dyn Db, - ) -> Result<(Type<'db>, Option), MetaclassError<'db>> { + ) -> Result<(Type<'db>, Option>), MetaclassError<'db>> { tracing::trace!("ClassLiteral::try_metaclass: {}", self.name(db)); // Identify the class's own metaclass (or take the first base class's metaclass). @@ -2268,14 +2268,17 @@ impl<'db> ClassLiteral<'db> { let transformer_params = if let CodeGeneratorKind::DataclassLike(Some(transformer_params)) = field_policy { - Some(DataclassParams::from(transformer_params)) + Some(DataclassParams::from_transformer_params( + db, + transformer_params, + )) } else { None }; let has_dataclass_param = |param| { - dataclass_params.is_some_and(|params| params.contains(param)) - || transformer_params.is_some_and(|params| params.contains(param)) + dataclass_params.is_some_and(|params| params.flags(db).contains(param)) + || transformer_params.is_some_and(|params| params.flags(db).contains(param)) }; let instance_ty = @@ -2353,7 +2356,7 @@ impl<'db> ClassLiteral<'db> { } let is_kw_only = name == "__replace__" - || kw_only.unwrap_or(has_dataclass_param(DataclassParams::KW_ONLY)); + || kw_only.unwrap_or(has_dataclass_param(DataclassFlags::KW_ONLY)); let mut parameter = if is_kw_only { Parameter::keyword_only(field_name) @@ -2391,7 +2394,7 @@ impl<'db> ClassLiteral<'db> { match (field_policy, name) { (CodeGeneratorKind::DataclassLike(_), "__init__") => { - if !has_dataclass_param(DataclassParams::INIT) { + if !has_dataclass_param(DataclassFlags::INIT) { return None; } @@ -2406,7 +2409,7 @@ impl<'db> ClassLiteral<'db> { signature_from_fields(vec![cls_parameter], Some(Type::none(db))) } (CodeGeneratorKind::DataclassLike(_), "__lt__" | "__le__" | "__gt__" | "__ge__") => { - if !has_dataclass_param(DataclassParams::ORDER) { + if !has_dataclass_param(DataclassFlags::ORDER) { return None; } @@ -2457,7 +2460,7 @@ impl<'db> ClassLiteral<'db> { signature_from_fields(vec![self_parameter], Some(instance_ty)) } (CodeGeneratorKind::DataclassLike(_), "__setattr__") => { - if has_dataclass_param(DataclassParams::FROZEN) { + if has_dataclass_param(DataclassFlags::FROZEN) { let signature = Signature::new( Parameters::new([ Parameter::positional_or_keyword(Name::new_static("self")) @@ -2473,7 +2476,7 @@ impl<'db> ClassLiteral<'db> { None } (CodeGeneratorKind::DataclassLike(_), "__slots__") => { - has_dataclass_param(DataclassParams::SLOTS).then(|| { + has_dataclass_param(DataclassFlags::SLOTS).then(|| { let fields = self.fields(db, specialization, field_policy); let slots = fields.keys().map(|name| Type::string_literal(db, name)); Type::heterogeneous_tuple(db, slots) @@ -2897,7 +2900,7 @@ impl<'db> ClassLiteral<'db> { default_ty = Some(field.default_type(db)); if self .dataclass_params(db) - .map(|params| params.contains(DataclassParams::NO_FIELD_SPECIFIERS)) + .map(|params| params.field_specifiers(db).is_none(db)) .unwrap_or(false) { // This happens when constructing a `dataclass` with a `dataclass_transform` @@ -3621,7 +3624,7 @@ impl<'db> VarianceInferable<'db> for ClassLiteral<'db> { let is_frozen_dataclass = Program::get(db).python_version(db) <= PythonVersion::PY312 && self .dataclass_params(db) - .is_some_and(|params| params.contains(DataclassParams::FROZEN)); + .is_some_and(|params| params.flags(db).contains(DataclassFlags::FROZEN)); if is_namedtuple || is_frozen_dataclass { TypeVarVariance::Covariant } else { diff --git a/crates/ty_python_semantic/src/types/function.rs b/crates/ty_python_semantic/src/types/function.rs index a94c222a37..dadafeede3 100644 --- a/crates/ty_python_semantic/src/types/function.rs +++ b/crates/ty_python_semantic/src/types/function.rs @@ -152,24 +152,32 @@ bitflags! { /// arguments that were passed in. For the precise meaning of the fields, see [1]. /// /// [1]: https://docs.python.org/3/library/typing.html#typing.dataclass_transform - #[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, salsa::Update)] - pub struct DataclassTransformerParams: u8 { + #[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, PartialOrd, Ord, salsa::Update)] + pub struct DataclassTransformerFlags: u8 { const EQ_DEFAULT = 1 << 0; const ORDER_DEFAULT = 1 << 1; const KW_ONLY_DEFAULT = 1 << 2; const FROZEN_DEFAULT = 1 << 3; - const FIELD_SPECIFIERS= 1 << 4; } } -impl get_size2::GetSize for DataclassTransformerParams {} +impl get_size2::GetSize for DataclassTransformerFlags {} -impl Default for DataclassTransformerParams { +impl Default for DataclassTransformerFlags { fn default() -> Self { Self::EQ_DEFAULT } } +#[salsa::interned(debug, heap_size=ruff_memory_usage::heap_size)] +#[derive(PartialOrd, Ord)] +pub struct DataclassTransformerParams<'db> { + pub flags: DataclassTransformerFlags, + pub field_specifiers: Type<'db>, +} + +impl get_size2::GetSize for DataclassTransformerParams<'_> {} + /// Representation of a function definition in the AST: either a non-generic function, or a generic /// function that has not been specialized. /// @@ -201,7 +209,7 @@ pub struct OverloadLiteral<'db> { /// The arguments to `dataclass_transformer`, if this function was annotated /// with `@dataclass_transformer(...)`. - pub(crate) dataclass_transformer_params: Option, + pub(crate) dataclass_transformer_params: Option>, } // The Salsa heap is tracked separately. @@ -212,7 +220,7 @@ impl<'db> OverloadLiteral<'db> { fn with_dataclass_transformer_params( self, db: &'db dyn Db, - params: DataclassTransformerParams, + params: DataclassTransformerParams<'db>, ) -> Self { Self::new( db, @@ -740,7 +748,7 @@ impl<'db> FunctionType<'db> { pub(crate) fn with_dataclass_transformer_params( self, db: &'db dyn Db, - params: DataclassTransformerParams, + params: DataclassTransformerParams<'db>, ) -> Self { // A decorator only applies to the specific overload that it is attached to, not to all // previous overloads. diff --git a/crates/ty_python_semantic/src/types/infer/builder.rs b/crates/ty_python_semantic/src/types/infer/builder.rs index 16b669e65a..fd274da3ca 100644 --- a/crates/ty_python_semantic/src/types/infer/builder.rs +++ b/crates/ty_python_semantic/src/types/infer/builder.rs @@ -2567,7 +2567,7 @@ impl<'db, 'ast> TypeInferenceBuilder<'db, 'ast> { .as_function_literal() .is_some_and(|function| function.is_known(self.db(), KnownFunction::Dataclass)) { - dataclass_params = Some(DataclassParams::default()); + dataclass_params = Some(DataclassParams::default_params(self.db())); continue; } @@ -2588,11 +2588,14 @@ impl<'db, 'ast> TypeInferenceBuilder<'db, 'ast> { // overload, or an overload and the implementation both. Nevertheless, this is not // allowed. We do not try to treat the offenders intelligently -- just use the // params of the last seen usage of `@dataclass_transform` - let params = f + let transformer_params = f .iter_overloads_and_implementation(self.db()) .find_map(|overload| overload.dataclass_transformer_params(self.db())); - if let Some(params) = params { - dataclass_params = Some(params.into()); + if let Some(transformer_params) = transformer_params { + dataclass_params = Some(DataclassParams::from_transformer_params( + self.db(), + transformer_params, + )); continue; } } diff --git a/crates/ty_python_semantic/src/types/type_ordering.rs b/crates/ty_python_semantic/src/types/type_ordering.rs index f331aefa63..d561e6a8b8 100644 --- a/crates/ty_python_semantic/src/types/type_ordering.rs +++ b/crates/ty_python_semantic/src/types/type_ordering.rs @@ -83,15 +83,11 @@ pub(super) fn union_or_intersection_elements_ordering<'db>( (Type::WrapperDescriptor(_), _) => Ordering::Less, (_, Type::WrapperDescriptor(_)) => Ordering::Greater, - (Type::DataclassDecorator(left), Type::DataclassDecorator(right)) => { - left.bits().cmp(&right.bits()) - } + (Type::DataclassDecorator(left), Type::DataclassDecorator(right)) => left.cmp(right), (Type::DataclassDecorator(_), _) => Ordering::Less, (_, Type::DataclassDecorator(_)) => Ordering::Greater, - (Type::DataclassTransformer(left), Type::DataclassTransformer(right)) => { - left.bits().cmp(&right.bits()) - } + (Type::DataclassTransformer(left), Type::DataclassTransformer(right)) => left.cmp(right), (Type::DataclassTransformer(_), _) => Ordering::Less, (_, Type::DataclassTransformer(_)) => Ordering::Greater,