Skip to content

Commit 43eddc5

Browse files
authored
[ty] Improve and extend tests for instance attributes redeclared in subclasses (#20866)
Part of astral-sh/ty#1345
1 parent f8e00e3 commit 43eddc5

File tree

1 file changed

+29
-2
lines changed

1 file changed

+29
-2
lines changed

crates/ty_python_semantic/resources/mdtest/attributes.md

Lines changed: 29 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -828,6 +828,10 @@ class Base:
828828
redeclared_with_narrower_type: str | None
829829
redeclared_with_wider_type: str | None
830830

831+
redeclared_in_method_with_same_type: str | None
832+
redeclared_in_method_with_narrower_type: str | None
833+
redeclared_in_method_with_wider_type: str | None
834+
831835
overwritten_in_subclass_body: str
832836
overwritten_in_subclass_method: str
833837

@@ -873,6 +877,14 @@ class Intermediate(Base):
873877
undeclared = "intermediate"
874878

875879
def set_attributes(self) -> None:
880+
self.redeclared_in_method_with_same_type: str | None = None
881+
882+
# TODO: This should be an error (violates Liskov)
883+
self.redeclared_in_method_with_narrower_type: str = "foo"
884+
885+
# TODO: This should be an error (violates Liskov)
886+
self.redeclared_in_method_with_wider_type: object = object()
887+
876888
# TODO: This should be an `invalid-assignment` error
877889
self.overwritten_in_subclass_method = None
878890

@@ -889,18 +901,33 @@ reveal_type(Derived().attribute) # revealed: int | None
889901
reveal_type(Derived.redeclared_with_same_type) # revealed: str | None
890902
reveal_type(Derived().redeclared_with_same_type) # revealed: str | None
891903

892-
# TODO: It would probably be more consistent if these were `str | None`
904+
# We infer the narrower type here, despite the Liskov violation,
905+
# for compatibility with other type checkers (and to reflect the clear user intent)
893906
reveal_type(Derived.redeclared_with_narrower_type) # revealed: str
894907
reveal_type(Derived().redeclared_with_narrower_type) # revealed: str
895908

896-
# TODO: It would probably be more consistent if these were `str | None`
909+
# We infer the wider type here, despite the Liskov violation,
910+
# for compatibility with other type checkers (and to reflect the clear user intent)
897911
reveal_type(Derived.redeclared_with_wider_type) # revealed: str | int | None
898912
reveal_type(Derived().redeclared_with_wider_type) # revealed: str | int | None
899913

900914
# TODO: Both of these should be `str`
901915
reveal_type(Derived.overwritten_in_subclass_body) # revealed: Unknown | None
902916
reveal_type(Derived().overwritten_in_subclass_body) # revealed: Unknown | None | str
903917

918+
reveal_type(Derived.redeclared_in_method_with_same_type) # revealed: str | None
919+
reveal_type(Derived().redeclared_in_method_with_same_type) # revealed: str | None
920+
921+
# TODO: both of these should be `str`, despite the Liskov violation,
922+
# for compatibility with other type checkers (and to reflect the clear user intent)
923+
reveal_type(Derived.redeclared_in_method_with_narrower_type) # revealed: str | None
924+
reveal_type(Derived().redeclared_in_method_with_narrower_type) # revealed: str | None
925+
926+
# TODO: both of these should be `object`, despite the Liskov violation,
927+
# for compatibility with other type checkers (and to reflect the clear user intent)
928+
reveal_type(Derived.redeclared_in_method_with_wider_type) # revealed: str | None
929+
reveal_type(Derived().redeclared_in_method_with_wider_type) # revealed: object
930+
904931
reveal_type(Derived.overwritten_in_subclass_method) # revealed: str
905932
reveal_type(Derived().overwritten_in_subclass_method) # revealed: str
906933

0 commit comments

Comments
 (0)