diff --git a/crates/red_knot_python_semantic/resources/mdtest/call/dunder.md b/crates/red_knot_python_semantic/resources/mdtest/call/dunder.md index 7f8cb0d564..1f31f8bbdd 100644 --- a/crates/red_knot_python_semantic/resources/mdtest/call/dunder.md +++ b/crates/red_knot_python_semantic/resources/mdtest/call/dunder.md @@ -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 diff --git a/crates/red_knot_python_semantic/src/types.rs b/crates/red_knot_python_semantic/src/types.rs index 98da9d3aba..eac89fe2e2 100644 --- a/crates/red_knot_python_semantic/src/types.rs +++ b/crates/red_knot_python_semantic/src/types.rs @@ -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(),