Attempt to model getattr_static on gradual types
This commit is contained in:
@@ -104,4 +104,30 @@ inspect.getattr_static(C(), 1)
|
||||
inspect.getattr_static(C(), "x", "default-arg", "one too many")
|
||||
```
|
||||
|
||||
## Possibly unbound attributes
|
||||
|
||||
```py
|
||||
import inspect
|
||||
|
||||
def _(flag: bool):
|
||||
class C:
|
||||
if flag:
|
||||
x: int = 1
|
||||
|
||||
reveal_type(inspect.getattr_static(C, "x", "default")) # revealed: int | Literal["default"]
|
||||
```
|
||||
|
||||
## Gradual types
|
||||
|
||||
```py
|
||||
import inspect
|
||||
from typing import Any
|
||||
|
||||
def _(a: Any, tuple_of_any: tuple[Any]):
|
||||
reveal_type(inspect.getattr_static(a, "x", "default")) # revealed: Any | Literal["default"]
|
||||
|
||||
# TODO: Ideally, this would just be `Literal[index]`
|
||||
reveal_type(inspect.getattr_static(tuple_of_any, "index", "default")) # revealed: Literal[index] | Literal["default"]
|
||||
```
|
||||
|
||||
[official documentation]: https://docs.python.org/3/library/inspect.html#inspect.getattr_static
|
||||
|
||||
@@ -2037,11 +2037,29 @@ impl<'db> Type<'db> {
|
||||
default
|
||||
};
|
||||
|
||||
let static_member = instance_ty
|
||||
.static_member(db, attr_name.value(db))
|
||||
.ignore_possibly_unbound() // TODO: we could emit a diagnostic here (if default is not set)
|
||||
.unwrap_or(default);
|
||||
binding.set_return_type(static_member);
|
||||
let union_with_default = |ty| UnionType::from_elements(db, [ty, default]);
|
||||
|
||||
// TODO: we could emit a diagnostic here (if default is not set)
|
||||
binding.set_return_type(
|
||||
match instance_ty.static_member(db, attr_name.value(db)) {
|
||||
Symbol::Type(ty, Boundness::Bound) => {
|
||||
if instance_ty.is_fully_static(db) {
|
||||
ty
|
||||
} else {
|
||||
// Here, we attempt to model the fact that an attribute lookup on
|
||||
// a non-fully static type could fail. This is an approximation,
|
||||
// as there are gradual types like `tuple[Any]`, on which a lookup
|
||||
// of (e.g. of the `index` method) would always succeed.
|
||||
|
||||
union_with_default(ty)
|
||||
}
|
||||
}
|
||||
Symbol::Type(ty, Boundness::PossiblyUnbound) => {
|
||||
union_with_default(ty)
|
||||
}
|
||||
Symbol::Unbound => default,
|
||||
},
|
||||
);
|
||||
}
|
||||
|
||||
_ => {}
|
||||
|
||||
Reference in New Issue
Block a user