[ty] Pass down specialization to generic dataclass bases (#19472)
Some checks are pending
CI / Determine changes (push) Waiting to run
CI / cargo fmt (push) Waiting to run
CI / cargo clippy (push) Blocked by required conditions
CI / cargo test (linux) (push) Blocked by required conditions
CI / cargo test (linux, release) (push) Blocked by required conditions
CI / cargo test (windows) (push) Blocked by required conditions
CI / cargo test (wasm) (push) Blocked by required conditions
CI / cargo build (release) (push) Waiting to run
CI / cargo build (msrv) (push) Blocked by required conditions
CI / cargo fuzz build (push) Blocked by required conditions
CI / fuzz parser (push) Blocked by required conditions
CI / test scripts (push) Blocked by required conditions
CI / ecosystem (push) Blocked by required conditions
CI / Fuzz for new ty panics (push) Blocked by required conditions
CI / cargo shear (push) Blocked by required conditions
CI / python package (push) Waiting to run
CI / pre-commit (push) Waiting to run
CI / mkdocs (push) Waiting to run
CI / formatter instabilities and black similarity (push) Blocked by required conditions
CI / test ruff-lsp (push) Blocked by required conditions
CI / check playground (push) Blocked by required conditions
CI / benchmarks-instrumented (push) Blocked by required conditions
CI / benchmarks-walltime (push) Blocked by required conditions
[ty Playground] Release / publish (push) Waiting to run

## Summary

closes https://github.com/astral-sh/ty/issues/853

## Test Plan

Regression test
This commit is contained in:
David Peter 2025-07-21 20:51:58 +02:00 committed by GitHub
parent 88de5727df
commit fcdffe4ac9
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
2 changed files with 47 additions and 6 deletions

View file

@ -1668,16 +1668,16 @@ impl<'db> ClassLiteral<'db> {
if field_policy == CodeGeneratorKind::NamedTuple {
// NamedTuples do not allow multiple inheritance, so it is sufficient to enumerate the
// fields of this class only.
return self.own_fields(db);
return self.own_fields(db, specialization);
}
let matching_classes_in_mro: Vec<_> = self
.iter_mro(db, specialization)
.filter_map(|superclass| {
if let Some(class) = superclass.into_class() {
let class_literal = class.class_literal(db).0;
let (class_literal, specialization) = class.class_literal(db);
if field_policy.matches(db, class_literal) {
Some(class_literal)
Some((class_literal, specialization))
} else {
None
}
@ -1691,7 +1691,7 @@ impl<'db> ClassLiteral<'db> {
matching_classes_in_mro
.into_iter()
.rev()
.flat_map(|class| class.own_fields(db))
.flat_map(|(class, specialization)| class.own_fields(db, specialization))
// We collect into a FxOrderMap here to deduplicate attributes
.collect()
}
@ -1707,7 +1707,11 @@ impl<'db> ClassLiteral<'db> {
/// y: str = "a"
/// ```
/// we return a map `{"x": (int, None), "y": (str, Some(Literal["a"]))}`.
fn own_fields(self, db: &'db dyn Db) -> FxOrderMap<Name, (Type<'db>, Option<Type<'db>>)> {
fn own_fields(
self,
db: &'db dyn Db,
specialization: Option<Specialization<'db>>,
) -> FxOrderMap<Name, (Type<'db>, Option<Type<'db>>)> {
let mut attributes = FxOrderMap::default();
let class_body_scope = self.body_scope(db);
@ -1747,7 +1751,14 @@ impl<'db> ClassLiteral<'db> {
let bindings = use_def.end_of_scope_bindings(place_id);
let default_ty = place_from_bindings(db, bindings).ignore_possibly_unbound();
attributes.insert(place_expr.expect_name().clone(), (attr_ty, default_ty));
attributes.insert(
place_expr.expect_name().clone(),
(
attr_ty.apply_optional_specialization(db, specialization),
default_ty
.map(|ty| ty.apply_optional_specialization(db, specialization)),
),
);
}
}
}