mirror of
https://github.com/astral-sh/ruff.git
synced 2025-09-29 13:25:17 +00:00
[red-knot] Precise inference for __class__
attributes on objects of all types (#14921)
This commit is contained in:
parent
a54353392f
commit
c361cf66ad
3 changed files with 45 additions and 5 deletions
|
@ -115,3 +115,43 @@ def _(flag: bool):
|
||||||
# error: [unresolved-attribute] "Type `Literal[C1, C2]` has no attribute `x`"
|
# error: [unresolved-attribute] "Type `Literal[C1, C2]` has no attribute `x`"
|
||||||
reveal_type(C.x) # revealed: Unknown
|
reveal_type(C.x) # revealed: Unknown
|
||||||
```
|
```
|
||||||
|
|
||||||
|
## Objects of all types have a `__class__` method
|
||||||
|
|
||||||
|
```py
|
||||||
|
import typing
|
||||||
|
|
||||||
|
reveal_type(typing.__class__) # revealed: Literal[ModuleType]
|
||||||
|
|
||||||
|
a = 42
|
||||||
|
reveal_type(a.__class__) # revealed: Literal[int]
|
||||||
|
|
||||||
|
b = "42"
|
||||||
|
reveal_type(b.__class__) # revealed: Literal[str]
|
||||||
|
|
||||||
|
c = b"42"
|
||||||
|
reveal_type(c.__class__) # revealed: Literal[bytes]
|
||||||
|
|
||||||
|
d = True
|
||||||
|
reveal_type(d.__class__) # revealed: Literal[bool]
|
||||||
|
|
||||||
|
e = (42, 42)
|
||||||
|
reveal_type(e.__class__) # revealed: Literal[tuple]
|
||||||
|
|
||||||
|
def f(a: int, b: typing.LiteralString, c: int | str, d: type[str]):
|
||||||
|
reveal_type(a.__class__) # revealed: type[int]
|
||||||
|
reveal_type(b.__class__) # revealed: Literal[str]
|
||||||
|
reveal_type(c.__class__) # revealed: type[int] | type[str]
|
||||||
|
|
||||||
|
# `type[type]`, a.k.a., either the class `type` or some subclass of `type`.
|
||||||
|
# It would be incorrect to infer `Literal[type]` here,
|
||||||
|
# as `c` could be some subclass of `str` with a custom metaclass.
|
||||||
|
# All we know is that the metaclass must be a (non-strict) subclass of `type`.
|
||||||
|
reveal_type(d.__class__) # revealed: type[type]
|
||||||
|
|
||||||
|
reveal_type(f.__class__) # revealed: Literal[FunctionType]
|
||||||
|
|
||||||
|
class Foo: ...
|
||||||
|
|
||||||
|
reveal_type(Foo.__class__) # revealed: Literal[type]
|
||||||
|
```
|
||||||
|
|
|
@ -59,7 +59,7 @@ reveal_type(typing.__init__) # revealed: Literal[__init__]
|
||||||
# These come from `builtins.object`, not `types.ModuleType`:
|
# These come from `builtins.object`, not `types.ModuleType`:
|
||||||
reveal_type(typing.__eq__) # revealed: Literal[__eq__]
|
reveal_type(typing.__eq__) # revealed: Literal[__eq__]
|
||||||
|
|
||||||
reveal_type(typing.__class__) # revealed: Literal[type]
|
reveal_type(typing.__class__) # revealed: Literal[ModuleType]
|
||||||
|
|
||||||
# TODO: needs support for attribute access on instances, properties and generics;
|
# TODO: needs support for attribute access on instances, properties and generics;
|
||||||
# should be `dict[str, Any]`
|
# should be `dict[str, Any]`
|
||||||
|
|
|
@ -1272,6 +1272,10 @@ impl<'db> Type<'db> {
|
||||||
/// as accessed from instances of the `Bar` class.
|
/// as accessed from instances of the `Bar` class.
|
||||||
#[must_use]
|
#[must_use]
|
||||||
pub(crate) fn member(&self, db: &'db dyn Db, name: &str) -> Symbol<'db> {
|
pub(crate) fn member(&self, db: &'db dyn Db, name: &str) -> Symbol<'db> {
|
||||||
|
if name == "__class__" {
|
||||||
|
return self.to_meta_type(db).into();
|
||||||
|
}
|
||||||
|
|
||||||
match self {
|
match self {
|
||||||
Type::Any => Type::Any.into(),
|
Type::Any => Type::Any.into(),
|
||||||
Type::Never => {
|
Type::Never => {
|
||||||
|
@ -2697,10 +2701,6 @@ impl<'db> Class<'db> {
|
||||||
return Type::tuple(db, &tuple_elements).into();
|
return Type::tuple(db, &tuple_elements).into();
|
||||||
}
|
}
|
||||||
|
|
||||||
if name == "__class__" {
|
|
||||||
return self.metaclass(db).into();
|
|
||||||
}
|
|
||||||
|
|
||||||
for superclass in self.iter_mro(db) {
|
for superclass in self.iter_mro(db) {
|
||||||
match superclass {
|
match superclass {
|
||||||
// TODO we may instead want to record the fact that we encountered dynamic, and intersect it with
|
// TODO we may instead want to record the fact that we encountered dynamic, and intersect it with
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue