Skip to content

Commit 728d3ec

Browse files
[ty] Detect overloads decorated with @dataclass_transform
1 parent 101e1a5 commit 728d3ec

File tree

2 files changed

+45
-24
lines changed

2 files changed

+45
-24
lines changed

crates/ty_python_semantic/resources/mdtest/dataclass_transform.md

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -283,11 +283,11 @@ class D2:
283283

284284
# TODO: these should not be errors
285285
D1("a") # error: [too-many-positional-arguments]
286-
D2("a") # error: [too-many-positional-arguments]
286+
D2("a")
287287

288288
# TODO: these should be invalid-argument-type errors
289289
D1(1.2) # error: [too-many-positional-arguments]
290-
D2(1.2) # error: [too-many-positional-arguments]
290+
D2(1.2) # error: [invalid-argument-type]
291291
```
292292

293293
[`typing.dataclass_transform`]: https://docs.python.org/3/library/typing.html#typing.dataclass_transform

crates/ty_python_semantic/src/types/call/bind.rs

Lines changed: 43 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -20,8 +20,8 @@ use crate::types::generics::{Specialization, SpecializationBuilder, Specializati
2020
use crate::types::signatures::{Parameter, ParameterForm};
2121
use crate::types::{
2222
todo_type, BoundMethodType, DataclassParams, DataclassTransformerParams, FunctionDecorators,
23-
KnownClass, KnownFunction, KnownInstanceType, MethodWrapperKind, PropertyInstanceType,
24-
TupleType, UnionType, WrapperDescriptorKind,
23+
FunctionType, KnownClass, KnownFunction, KnownInstanceType, MethodWrapperKind,
24+
PropertyInstanceType, TupleType, UnionType, WrapperDescriptorKind,
2525
};
2626
use ruff_db::diagnostic::{Annotation, Severity, SubDiagnostic};
2727
use ruff_python_ast as ast;
@@ -770,29 +770,50 @@ impl<'db> Bindings<'db> {
770770
}
771771

772772
_ => {
773-
if let Some(params) = function_type.dataclass_transformer_params(db) {
774-
// This is a call to a custom function that was decorated with `@dataclass_transformer`.
775-
// If this function was called with a keyword argument like `order=False`, we extract
776-
// the argument type and overwrite the corresponding flag in `dataclass_params` after
777-
// constructing them from the `dataclass_transformer`-parameter defaults.
778-
779-
let mut dataclass_params = DataclassParams::from(params);
773+
let mut handle_dataclass_transformer_params =
774+
|function_type: &FunctionType| {
775+
if let Some(params) =
776+
function_type.dataclass_transformer_params(db)
777+
{
778+
// This is a call to a custom function that was decorated with `@dataclass_transformer`.
779+
// If this function was called with a keyword argument like `order=False`, we extract
780+
// the argument type and overwrite the corresponding flag in `dataclass_params` after
781+
// constructing them from the `dataclass_transformer`-parameter defaults.
782+
783+
let mut dataclass_params = DataclassParams::from(params);
784+
785+
if let Some(Some(Type::BooleanLiteral(order))) =
786+
callable_signature.iter().nth(overload_index).and_then(
787+
|signature| {
788+
let (idx, _) = signature
789+
.parameters()
790+
.keyword_by_name("order")?;
791+
overload.parameter_types().get(idx)
792+
},
793+
)
794+
{
795+
dataclass_params.set(DataclassParams::ORDER, *order);
796+
}
780797

781-
if let Some(Some(Type::BooleanLiteral(order))) = callable_signature
798+
overload.set_return_type(Type::DataclassDecorator(
799+
dataclass_params,
800+
));
801+
}
802+
};
803+
804+
// Ideally, either the implementation, or exactly one of the overloads
805+
// of the function can have the dataclass_transform decorator applied.
806+
// However, we do not yet enforce this, and in the case of multiple
807+
// applications of the decorator, we will only consider the last one
808+
// for the return value, since the prior ones will be over-written.
809+
if let Some(overloaded) = function_type.to_overloaded(db) {
810+
overloaded
811+
.overloads
782812
.iter()
783-
.nth(overload_index)
784-
.and_then(|signature| {
785-
let (idx, _) =
786-
signature.parameters().keyword_by_name("order")?;
787-
overload.parameter_types().get(idx)
788-
})
789-
{
790-
dataclass_params.set(DataclassParams::ORDER, *order);
791-
}
813+
.for_each(&mut handle_dataclass_transformer_params);
814+
};
792815

793-
overload
794-
.set_return_type(Type::DataclassDecorator(dataclass_params));
795-
}
816+
handle_dataclass_transformer_params(&function_type);
796817
}
797818
},
798819

0 commit comments

Comments
 (0)