Skip to content

Commit 79fbd20

Browse files
committed
[ty] Prefer declared base class attribute over inferred attribute on subclass
1 parent 6b94e62 commit 79fbd20

File tree

2 files changed

+40
-1
lines changed

2 files changed

+40
-1
lines changed

crates/ty_python_semantic/resources/mdtest/attributes.md

Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -865,6 +865,42 @@ reveal_type(Derived().declared_in_body) # revealed: int | None
865865
reveal_type(Derived().defined_in_init) # revealed: str | None
866866
```
867867

868+
#### Incorrect assignments in derived classes
869+
870+
```py
871+
class Base:
872+
declared_on_body_overwritten_on_body: str
873+
declared_on_body_overwritten_in_init: str
874+
875+
def __init__(self) -> None:
876+
self.declared_in_init_overwritten_on_body: str
877+
self.declared_in_init_overwritten_in_init: str
878+
879+
class Derived(Base):
880+
# TODO: this should be an error
881+
declared_on_body_overwritten_on_body = None
882+
883+
# TODO: this should be an error
884+
declared_in_init_overwritten_on_body = None
885+
886+
def f(self) -> None:
887+
# TODO: this should be an error
888+
self.declared_on_body_overwritten_in_init = None
889+
890+
# TODO: this should be an error
891+
self.declared_in_init_overwritten_in_init = None
892+
893+
reveal_type(Derived().declared_on_body_overwritten_in_init) # revealed: str
894+
895+
reveal_type(Derived().declared_in_init_overwritten_in_init) # revealed: str
896+
897+
# TODO: This should be `str`
898+
reveal_type(Derived().declared_on_body_overwritten_on_body) # revealed: Unknown | None | str
899+
900+
# TODO: This should be `str`
901+
reveal_type(Derived().declared_in_init_overwritten_on_body) # revealed: Unknown | None | str
902+
```
903+
868904
## Accessing attributes on class objects
869905

870906
When accessing attributes on class objects, they are always looked up on the type of the class

crates/ty_python_semantic/src/types/class.rs

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2863,7 +2863,10 @@ impl<'db> ClassLiteral<'db> {
28632863
let mut union = UnionBuilder::new(db);
28642864
let mut union_qualifiers = TypeQualifiers::empty();
28652865

2866-
for superclass in self.iter_mro(db, specialization) {
2866+
let mro = self.try_mro(db, specialization);
2867+
let mro = mro.unwrap_or_else(|e| e.fallback_mro());
2868+
2869+
for superclass in mro.iter().rev() {
28672870
match superclass {
28682871
ClassBase::Generic | ClassBase::Protocol => {
28692872
// Skip over these very special class bases that aren't really classes.

0 commit comments

Comments
 (0)