mirror of https://github.com/astral-sh/ruff
[ty] Store `field_specifiers`
This commit is contained in:
parent
8817ea5c84
commit
4dc88a0a5f
|
|
@ -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<DataclassTransformerParams> for DataclassParams {
|
||||
fn from(params: DataclassTransformerParams) -> Self {
|
||||
impl From<DataclassTransformerFlags> 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
|
||||
|
|
|
|||
|
|
@ -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),
|
||||
))
|
||||
},
|
||||
)
|
||||
})
|
||||
|
|
|
|||
|
|
@ -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<DataclassTransformerParams>), MetaclassError<'db>>,
|
||||
Result<(Type<'db>, Option<DataclassTransformerParams<'db>>), 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<DataclassTransformerParams>), MetaclassError<'db>> {
|
||||
) -> Result<(Type<'db>, Option<DataclassTransformerParams<'db>>), 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<DataclassTransformerParams>),
|
||||
DataclassLike(Option<DataclassTransformerParams<'db>>),
|
||||
/// 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<Self> {
|
||||
impl<'db> CodeGeneratorKind<'db> {
|
||||
pub(crate) fn from_class(db: &'db dyn Db, class: ClassLiteral<'db>) -> Option<Self> {
|
||||
#[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<CodeGeneratorKind> {
|
||||
) -> Option<CodeGeneratorKind<'db>> {
|
||||
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<CodeGeneratorKind> {
|
||||
fn code_generator_of_class_initial<'db>(
|
||||
_db: &'db dyn Db,
|
||||
_class: ClassLiteral<'db>,
|
||||
) -> Option<CodeGeneratorKind<'db>> {
|
||||
None
|
||||
}
|
||||
|
||||
#[expect(clippy::ref_option, clippy::trivially_copy_pass_by_ref)]
|
||||
fn code_generator_of_class_recover(
|
||||
_db: &dyn Db,
|
||||
_value: &Option<CodeGeneratorKind>,
|
||||
#[expect(clippy::ref_option)]
|
||||
fn code_generator_of_class_recover<'db>(
|
||||
_db: &'db dyn Db,
|
||||
_value: &Option<CodeGeneratorKind<'db>>,
|
||||
_count: u32,
|
||||
_class: ClassLiteral<'_>,
|
||||
) -> salsa::CycleRecoveryAction<Option<CodeGeneratorKind>> {
|
||||
_class: ClassLiteral<'db>,
|
||||
) -> salsa::CycleRecoveryAction<Option<CodeGeneratorKind<'db>>> {
|
||||
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<DeprecatedInstance<'db>>,
|
||||
|
||||
pub(crate) dataclass_params: Option<DataclassParams>,
|
||||
pub(crate) dataclass_transformer_params: Option<DataclassTransformerParams>,
|
||||
pub(crate) dataclass_params: Option<DataclassParams<'db>>,
|
||||
pub(crate) dataclass_transformer_params: Option<DataclassTransformerParams<'db>>,
|
||||
}
|
||||
|
||||
// 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<DataclassTransformerParams>), MetaclassError<'db>> {
|
||||
) -> Result<(Type<'db>, Option<DataclassTransformerParams<'db>>), 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 {
|
||||
|
|
|
|||
|
|
@ -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<DataclassTransformerParams>,
|
||||
pub(crate) dataclass_transformer_params: Option<DataclassTransformerParams<'db>>,
|
||||
}
|
||||
|
||||
// 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.
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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,
|
||||
|
||||
|
|
|
|||
Loading…
Reference in New Issue