Skip to content

Commit a9c5dac

Browse files
committed
Emit [mutable-override] for covariant override of union with method
1 parent 3eb7e2a commit a9c5dac

File tree

2 files changed

+52
-6
lines changed

2 files changed

+52
-6
lines changed

mypy/checker.py

+18-2
Original file line numberDiff line numberDiff line change
@@ -2146,13 +2146,15 @@ def check_method_override_for_base_with_name(
21462146
override_class_or_static,
21472147
context,
21482148
)
2149+
# Check if this override is covariant.
21492150
if (
21502151
ok
21512152
and original_node
21522153
and codes.MUTABLE_OVERRIDE in self.options.enabled_error_codes
21532154
and self.is_writable_attribute(original_node)
21542155
and not is_subtype(original_type, typ, ignore_pos_arg_names=True)
21552156
):
2157+
# Covariant override of mutable attribute.
21562158
base_str, override_str = format_type_distinctly(
21572159
original_type, typ, options=self.options
21582160
)
@@ -2162,10 +2164,24 @@ def check_method_override_for_base_with_name(
21622164
)
21632165
self.fail(msg, context)
21642166
elif isinstance(original_type, UnionType) and any(
2165-
is_subtype(orig_typ, typ, ignore_pos_arg_names=True)
2167+
is_subtype(typ, orig_typ, ignore_pos_arg_names=True)
21662168
for orig_typ in original_type.items
21672169
):
2168-
pass
2170+
# This method is a subtype of at least one union variant.
2171+
if (
2172+
original_node
2173+
and codes.MUTABLE_OVERRIDE in self.options.enabled_error_codes
2174+
and self.is_writable_attribute(original_node)
2175+
):
2176+
# Covariant override of mutable attribute.
2177+
base_str, override_str = format_type_distinctly(
2178+
original_type, typ, options=self.options
2179+
)
2180+
msg = message_registry.COVARIANT_OVERRIDE_OF_MUTABLE_ATTRIBUTE.with_additional_msg(
2181+
f' (base class "{base.name}" defined the type as {base_str},'
2182+
f" override has type {override_str})"
2183+
)
2184+
self.fail(msg, context)
21692185
elif is_equivalent(original_type, typ):
21702186
# Assume invariance for a non-callable attribute here. Note
21712187
# that this doesn't affect read-only properties which can have

test-data/unit/check-classes.test

+34-4
Original file line numberDiff line numberDiff line change
@@ -779,21 +779,51 @@ class B(A):
779779
[case testOverrideCallableUnionAttributeWithMethod]
780780
from typing import Callable, Union
781781

782-
class Base:
782+
class A:
783783
f1: Union[Callable[[str], str], str]
784784
f2: Union[Callable[[str], str], str]
785785
f3: Union[Callable[[str], str], str]
786+
f4: Union[Callable[[str], str], str]
786787

787-
class Derived(Base):
788+
class B(A):
788789
def f1(self, x: str) -> str:
789790
pass
790791

792+
def f2(self, x: object) -> str:
793+
pass
794+
795+
@classmethod
796+
def f3(cls, x: str) -> str:
797+
pass
798+
799+
@staticmethod
800+
def f4(x: str) -> str:
801+
pass
802+
[builtins fixtures/classmethod.pyi]
803+
804+
[case testOverrideCallableUnionAttributeWithMethodMutableOverride]
805+
# flags: --enable-error-code=mutable-override
806+
from typing import Callable, Union
807+
808+
class A:
809+
f1: Union[Callable[[str], str], str]
810+
f2: Union[Callable[[str], str], str]
811+
f3: Union[Callable[[str], str], str]
812+
f4: Union[Callable[[str], str], str]
813+
814+
class B(A):
815+
def f1(self, x: str) -> str: # E: Covariant override of a mutable attribute (base class "A" defined the type as "Union[Callable[[str], str], str]", override has type "Callable[[str], str]")
816+
pass
817+
818+
def f2(self, x: object) -> str: # E: Covariant override of a mutable attribute (base class "A" defined the type as "Union[Callable[[str], str], str]", override has type "Callable[[object], str]")
819+
pass
820+
791821
@classmethod
792-
def f2(cls, x: str) -> str:
822+
def f3(cls, x: str) -> str: # E: Covariant override of a mutable attribute (base class "A" defined the type as "Union[Callable[[str], str], str]", override has type "Callable[[str], str]")
793823
pass
794824

795825
@staticmethod
796-
def f3(x: str) -> str:
826+
def f4(x: str) -> str: # E: Covariant override of a mutable attribute (base class "A" defined the type as "Union[Callable[[str], str], str]", override has type "Callable[[str], str]")
797827
pass
798828
[builtins fixtures/classmethod.pyi]
799829

0 commit comments

Comments
 (0)