Fix member lookup for unions & intersections ignoring policy (#17066)

## Summary

A quick fix for how union/intersection member search ins performed in
Knot.

## Test Plan

* Added a dunder method call test for Union, which exhibits the error
* Also added an intersection error, but it is not triggering currently
due to `call` logic not being fully implemented for intersections.

---------

Co-authored-by: David Peter <mail@david-peter.de>
This commit is contained in:
Mike Perlov 2025-03-31 09:40:47 +02:00 committed by GitHub
parent 5f83a32553
commit 5c1fab0661
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
2 changed files with 30 additions and 2 deletions

View file

@ -204,6 +204,28 @@ def _(flag: bool):
reveal_type(d[0]) # revealed: str | bytes
```
## Calling a union of types without dunder methods
We add instance attributes here to make sure that we don't treat the implicit dunder calls here like
regular method calls.
```py
def external_getitem(instance, key: int) -> str:
return str(key)
class NotSubscriptable1:
def __init__(self, value: int):
self.__getitem__ = external_getitem
class NotSubscriptable2:
def __init__(self, value: int):
self.__getitem__ = external_getitem
def _(union: NotSubscriptable1 | NotSubscriptable2):
# error: [non-subscriptable]
union[0]
```
## Calling a possibly-unbound dunder method
```py

View file

@ -1963,11 +1963,17 @@ impl<'db> Type<'db> {
match self {
Type::Union(union) => union
.map_with_boundness(db, |elem| elem.member(db, &name).symbol)
.map_with_boundness(db, |elem| {
elem.member_lookup_with_policy(db, name_str.into(), policy)
.symbol
})
.into(),
Type::Intersection(intersection) => intersection
.map_with_boundness(db, |elem| elem.member(db, &name).symbol)
.map_with_boundness(db, |elem| {
elem.member_lookup_with_policy(db, name_str.into(), policy)
.symbol
})
.into(),
Type::Dynamic(..) | Type::Never => Symbol::bound(self).into(),