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