[ty] dataclasses: Allow using dataclasses.dataclass as a function. (#18440)

## Summary

Part of https://github.com/astral-sh/ty/issues/111

Using `dataclass` as a function, instead of as a decorator did not work
as expected prior to this.
Fix that by modifying the dataclass overload's return type.

## Test Plan

New mdtests, fixing the existing TODO.
This commit is contained in:
Abhijeet Prasad Bodas 2025-06-03 22:20:29 +05:30 committed by GitHub
parent 2c3b3d3230
commit 71d8a5da2a
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
2 changed files with 31 additions and 3 deletions

View file

@ -797,7 +797,20 @@ C(1) < C(2) # ok
### Using `dataclass` as a function ### Using `dataclass` as a function
To do ```py
from dataclasses import dataclass
class B:
x: int
# error: [missing-argument]
dataclass(B)()
# error: [invalid-argument-type]
dataclass(B)("a")
reveal_type(dataclass(B)(3).x) # revealed: int
```
## Internals ## Internals

View file

@ -22,8 +22,8 @@ use crate::types::function::{DataclassTransformerParams, FunctionDecorators, Kno
use crate::types::generics::{Specialization, SpecializationBuilder, SpecializationError}; use crate::types::generics::{Specialization, SpecializationBuilder, SpecializationError};
use crate::types::signatures::{Parameter, ParameterForm}; use crate::types::signatures::{Parameter, ParameterForm};
use crate::types::{ use crate::types::{
BoundMethodType, DataclassParams, KnownClass, KnownInstanceType, MethodWrapperKind, BoundMethodType, ClassLiteral, DataclassParams, KnownClass, KnownInstanceType,
PropertyInstanceType, SpecialFormType, TupleType, TypeMapping, UnionType, MethodWrapperKind, PropertyInstanceType, SpecialFormType, TupleType, TypeMapping, UnionType,
WrapperDescriptorKind, ide_support, todo_type, WrapperDescriptorKind, ide_support, todo_type,
}; };
use ruff_db::diagnostic::{Annotation, Diagnostic, Severity, SubDiagnostic}; use ruff_db::diagnostic::{Annotation, Diagnostic, Severity, SubDiagnostic};
@ -839,6 +839,21 @@ impl<'db> Bindings<'db> {
overload.set_return_type(Type::DataclassDecorator(params)); overload.set_return_type(Type::DataclassDecorator(params));
} }
// `dataclass` being used as a non-decorator
if let [Some(Type::ClassLiteral(class_literal))] =
overload.parameter_types()
{
let params = DataclassParams::default();
overload.set_return_type(Type::from(ClassLiteral::new(
db,
class_literal.name(db),
class_literal.body_scope(db),
class_literal.known(db),
Some(params),
class_literal.dataclass_transformer_params(db),
)));
}
} }
Some(KnownFunction::DataclassTransform) => { Some(KnownFunction::DataclassTransform) => {