mirror of
https://github.com/astral-sh/ruff.git
synced 2025-09-29 13:24:57 +00:00
[ty] Preserve qualifiers when accessing attributes on unions/intersections (#20114)
## Summary Properly preserve type qualifiers when accessing attributes on unions and intersections. This is a prerequisite for https://github.com/astral-sh/ruff/pull/19579. Also fix a completely wrong implementation of `map_with_boundness_and_qualifiers`. It now closely follows `map_with_boundness` (just above). ## Test Plan I thought about it, but didn't find any easy way to test this. This only affected `Type::member`. Things like validation of attribute writes (where type qualifiers like `ClassVar` and `Final` are important) were already handling things correctly.
This commit is contained in:
parent
ce1dc21e7e
commit
0b3548755c
2 changed files with 16 additions and 22 deletions
|
@ -398,7 +398,7 @@ def f_okay(c: Callable[[], None]):
|
|||
c.__qualname__ = "my_callable"
|
||||
|
||||
result = getattr_static(c, "__qualname__")
|
||||
reveal_type(result) # revealed: Never
|
||||
reveal_type(result) # revealed: property
|
||||
if isinstance(result, property) and result.fset:
|
||||
c.__qualname__ = "my_callable" # okay
|
||||
```
|
||||
|
|
|
@ -3309,19 +3309,14 @@ impl<'db> Type<'db> {
|
|||
let name_str = name.as_str();
|
||||
|
||||
match self {
|
||||
Type::Union(union) => union
|
||||
.map_with_boundness(db, |elem| {
|
||||
elem.member_lookup_with_policy(db, name_str.into(), policy)
|
||||
.place
|
||||
})
|
||||
.into(),
|
||||
Type::Union(union) => union.map_with_boundness_and_qualifiers(db, |elem| {
|
||||
elem.member_lookup_with_policy(db, name_str.into(), policy)
|
||||
}),
|
||||
|
||||
Type::Intersection(intersection) => intersection
|
||||
.map_with_boundness(db, |elem| {
|
||||
.map_with_boundness_and_qualifiers(db, |elem| {
|
||||
elem.member_lookup_with_policy(db, name_str.into(), policy)
|
||||
.place
|
||||
})
|
||||
.into(),
|
||||
}),
|
||||
|
||||
Type::Dynamic(..) | Type::Never => Place::bound(self).into(),
|
||||
|
||||
|
@ -9743,8 +9738,8 @@ impl<'db> IntersectionType<'db> {
|
|||
let mut builder = IntersectionBuilder::new(db);
|
||||
let mut qualifiers = TypeQualifiers::empty();
|
||||
|
||||
let mut any_unbound = false;
|
||||
let mut any_possibly_unbound = false;
|
||||
let mut all_unbound = true;
|
||||
let mut any_definitely_bound = false;
|
||||
for ty in self.positive_elements_or_object(db) {
|
||||
let PlaceAndQualifiers {
|
||||
place: member,
|
||||
|
@ -9752,12 +9747,11 @@ impl<'db> IntersectionType<'db> {
|
|||
} = transform_fn(&ty);
|
||||
qualifiers |= new_qualifiers;
|
||||
match member {
|
||||
Place::Unbound => {
|
||||
any_unbound = true;
|
||||
}
|
||||
Place::Unbound => {}
|
||||
Place::Type(ty_member, member_boundness) => {
|
||||
if member_boundness == Boundness::PossiblyUnbound {
|
||||
any_possibly_unbound = true;
|
||||
all_unbound = false;
|
||||
if member_boundness == Boundness::Bound {
|
||||
any_definitely_bound = true;
|
||||
}
|
||||
|
||||
builder = builder.add_positive(ty_member);
|
||||
|
@ -9766,15 +9760,15 @@ impl<'db> IntersectionType<'db> {
|
|||
}
|
||||
|
||||
PlaceAndQualifiers {
|
||||
place: if any_unbound {
|
||||
place: if all_unbound {
|
||||
Place::Unbound
|
||||
} else {
|
||||
Place::Type(
|
||||
builder.build(),
|
||||
if any_possibly_unbound {
|
||||
Boundness::PossiblyUnbound
|
||||
} else {
|
||||
if any_definitely_bound {
|
||||
Boundness::Bound
|
||||
} else {
|
||||
Boundness::PossiblyUnbound
|
||||
},
|
||||
)
|
||||
},
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue