Skip to content

Commit 096ace5

Browse files
committed
Allow inheritance
1 parent 3ee1d64 commit 096ace5

File tree

2 files changed

+27
-8
lines changed

2 files changed

+27
-8
lines changed

crates/ty_python_semantic/resources/mdtest/liskov.md

Lines changed: 16 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -59,9 +59,10 @@ class ValidSubclass(Base):
5959
### Regular class-body assignments
6060

6161
An unannotated class-body assignment is an instance variable with a class-level default. This means
62-
it can replace another inherited instance-variable default, but it cannot override an inherited
63-
`ClassVar`. An explicit `ClassVar` cannot override an inherited unannotated class-body assignment
64-
either, because code using the base class can still write that attribute through an instance:
62+
it can replace another inherited instance-variable default. If it overrides an inherited `ClassVar`,
63+
it inherits that declaration and remains a class variable. However, an explicit `ClassVar` cannot
64+
override an inherited unannotated class-body assignment, because code using the base class can still
65+
write that attribute through an instance:
6566

6667
```py
6768
from typing import ClassVar
@@ -71,7 +72,6 @@ class Base:
7172
class_attr: ClassVar[int] = 1
7273

7374
class RegularClassAttributeOverride(Base):
74-
# error: [invalid-method-override] "instance variable cannot override class variable `Base.class_attr`"
7575
class_attr = 1
7676

7777
class RegularClassAttributeBase:
@@ -183,9 +183,9 @@ class DC7(DC6):
183183

184184
### Protocol implementations
185185

186-
Regular class-body assignments can implement protocol instance attributes. They cannot implement a
187-
protocol `ClassVar` attribute, because the assignment still declares an instance variable with a
188-
class-level default:
186+
Regular class-body assignments can implement protocol instance attributes. They can also provide the
187+
value for protocol `ClassVar` attributes by inheriting the `ClassVar` declaration from the protocol
188+
member:
189189

190190
```py
191191
from typing import ClassVar, Protocol
@@ -196,10 +196,18 @@ class ProtocolBase(Protocol):
196196
instance_attr_with_default: int = 1
197197

198198
class ProtocolImpl(ProtocolBase):
199-
# error: [invalid-method-override] "instance variable cannot override class variable `ProtocolBase.class_attr`"
200199
class_attr = 1
201200
instance_attr = 1
202201
instance_attr_with_default = 1
202+
203+
class ProtocolWithClassVar(Protocol):
204+
x: int = 1
205+
y: int
206+
z: ClassVar[int]
207+
208+
class ProtocolWithClassVarImpl(ProtocolWithClassVar):
209+
y = 0
210+
z = 0
203211
```
204212

205213
### Imported unannotated assignments

crates/ty_python_semantic/src/types/overrides.rs

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -497,6 +497,17 @@ fn check_class_declaration<'db>(
497497
subclass_variable_kind,
498498
Some(superclass_variable_kind),
499499
) {
500+
// An unannotated class-body assignment can inherit an overridden `ClassVar`
501+
// declaration instead of introducing a conflicting instance variable.
502+
if invalid_override.subclass_kind == VariableKind::Instance
503+
&& invalid_override.superclass_kind == VariableKind::Class
504+
&& first_reachable_definition
505+
.kind(db)
506+
.is_unannotated_assignment()
507+
{
508+
continue;
509+
}
510+
500511
if let Some((immediate_parent, immediate_parent_kind)) =
501512
immediate_parent_variable_kind
502513
&& immediate_parent != superclass

0 commit comments

Comments
 (0)