Skip to content

Commit 24ecb27

Browse files
authored
Fix crash on deferred supertype and setter override (#18649)
Fixes #18648 The fix is straightforward, but unlike for getter I decided to not create any ad-hoc types during `last_pass`.
1 parent 64a5ccc commit 24ecb27

File tree

2 files changed

+37
-7
lines changed

2 files changed

+37
-7
lines changed

mypy/checker.py

+15-7
Original file line numberDiff line numberDiff line change
@@ -2147,16 +2147,11 @@ def check_method_override_for_base_with_name(
21472147
# it can be checked for compatibility.
21482148
original_type = get_proper_type(base_attr.type)
21492149
original_node = base_attr.node
2150-
always_allow_covariant = False
2151-
if is_settable_property(defn) and (
2152-
is_settable_property(original_node) or isinstance(original_node, Var)
2153-
):
2154-
if is_custom_settable_property(defn) or (is_custom_settable_property(original_node)):
2155-
always_allow_covariant = True
2156-
self.check_setter_type_override(defn, base_attr, base)
21572150
# `original_type` can be partial if (e.g.) it is originally an
21582151
# instance variable from an `__init__` block that becomes deferred.
2152+
supertype_ready = True
21592153
if original_type is None or isinstance(original_type, PartialType):
2154+
supertype_ready = False
21602155
if self.pass_num < self.last_pass:
21612156
# If there are passes left, defer this node until next pass,
21622157
# otherwise try reconstructing the method type from available information.
@@ -2179,6 +2174,19 @@ def check_method_override_for_base_with_name(
21792174
else:
21802175
# Will always fail to typecheck below, since we know the node is a method
21812176
original_type = NoneType()
2177+
2178+
always_allow_covariant = False
2179+
if is_settable_property(defn) and (
2180+
is_settable_property(original_node) or isinstance(original_node, Var)
2181+
):
2182+
if is_custom_settable_property(defn) or (is_custom_settable_property(original_node)):
2183+
# Unlike with getter, where we try to construct some fallback type in case of
2184+
# deferral during last_pass, we can't make meaningful setter checks if the
2185+
# supertype is not known precisely.
2186+
if supertype_ready:
2187+
always_allow_covariant = True
2188+
self.check_setter_type_override(defn, base_attr, base)
2189+
21822190
if isinstance(original_node, (FuncDef, OverloadedFuncDef)):
21832191
original_class_or_static = original_node.is_class or original_node.is_static
21842192
elif isinstance(original_node, Decorator):

test-data/unit/check-classes.test

+22
Original file line numberDiff line numberDiff line change
@@ -8443,3 +8443,25 @@ class C:
84438443
def x(self) -> None:
84448444
pass
84458445
[builtins fixtures/property.pyi]
8446+
8447+
[case testPropertySetterSuperclassDeferred]
8448+
from typing import Callable, TypeVar
8449+
8450+
class B:
8451+
def __init__(self) -> None:
8452+
self.foo = f()
8453+
8454+
class C(B):
8455+
@property
8456+
def foo(self) -> str: ...
8457+
@foo.setter # E: Incompatible override of a setter type \
8458+
# N: (base class "B" defined the type as "str", \
8459+
# N: override has type "int")
8460+
def foo(self, x: int) -> None: ...
8461+
8462+
T = TypeVar("T")
8463+
def deco(fn: Callable[[], list[T]]) -> Callable[[], T]: ...
8464+
8465+
@deco
8466+
def f() -> list[str]: ...
8467+
[builtins fixtures/property.pyi]

0 commit comments

Comments
 (0)