Skip to content

Commit fcf4c07

Browse files
committed
Use checkmember.py to check method override
1 parent 4fb187f commit fcf4c07

File tree

7 files changed

+74
-142
lines changed

7 files changed

+74
-142
lines changed

mypy/checker.py

Lines changed: 54 additions & 118 deletions
Original file line numberDiff line numberDiff line change
@@ -2134,40 +2134,17 @@ def check_method_or_accessor_override_for_base(
21342134
return None
21352135
return found_base_method
21362136

2137-
def check_setter_type_override(
2138-
self, defn: OverloadedFuncDef, base_attr: SymbolTableNode, base: TypeInfo
2139-
) -> None:
2137+
def check_setter_type_override(self, defn: OverloadedFuncDef, base: TypeInfo) -> None:
21402138
"""Check override of a setter type of a mutable attribute.
21412139
21422140
Currently, this should be only called when either base node or the current node
21432141
is a custom settable property (i.e. where setter type is different from getter type).
21442142
Note that this check is contravariant.
21452143
"""
2146-
base_node = base_attr.node
2147-
assert isinstance(base_node, (OverloadedFuncDef, Var))
2148-
original_type, is_original_setter = get_raw_setter_type(base_node)
2149-
if isinstance(base_node, Var):
2150-
expanded_type = map_type_from_supertype(original_type, defn.info, base)
2151-
original_type = get_proper_type(
2152-
expand_self_type(base_node, expanded_type, fill_typevars(defn.info))
2153-
)
2154-
else:
2155-
assert isinstance(original_type, ProperType)
2156-
assert isinstance(original_type, CallableType)
2157-
original_type = self.bind_and_map_method(base_attr, original_type, defn.info, base)
2158-
assert isinstance(original_type, CallableType)
2159-
if is_original_setter:
2160-
original_type = original_type.arg_types[0]
2161-
else:
2162-
original_type = original_type.ret_type
2163-
2164-
typ, is_setter = get_raw_setter_type(defn)
2165-
assert isinstance(typ, ProperType) and isinstance(typ, CallableType)
2166-
typ = bind_self(typ, self.scope.active_self_type())
2167-
if is_setter:
2168-
typ = typ.arg_types[0]
2169-
else:
2170-
typ = typ.ret_type
2144+
typ, _ = self.node_type_from_base(defn, defn.info, setter_type=True)
2145+
original_type, _ = self.node_type_from_base(defn, base, setter_type=True)
2146+
# The caller should handle deferrals.
2147+
assert typ is not None and original_type is not None
21712148

21722149
if not is_subtype(original_type, typ):
21732150
self.msg.incompatible_setter_override(defn.items[1], typ, original_type, base)
@@ -2192,28 +2169,20 @@ def check_method_override_for_base_with_name(
21922169
context = defn.func
21932170

21942171
# Construct the type of the overriding method.
2195-
# TODO: this logic is much less complete than similar one in checkmember.py
21962172
if isinstance(defn, (FuncDef, OverloadedFuncDef)):
2197-
typ: Type = self.function_type(defn)
21982173
override_class_or_static = defn.is_class or defn.is_static
2199-
override_class = defn.is_class
22002174
else:
2201-
assert defn.var.is_ready
2202-
assert defn.var.type is not None
2203-
typ = defn.var.type
22042175
override_class_or_static = defn.func.is_class or defn.func.is_static
2205-
override_class = defn.func.is_class
2206-
typ = get_proper_type(typ)
2207-
if isinstance(typ, FunctionLike) and not is_static(context):
2208-
typ = bind_self(typ, self.scope.active_self_type(), is_classmethod=override_class)
2209-
# Map the overridden method type to subtype context so that
2210-
# it can be checked for compatibility.
2211-
original_type = get_proper_type(base_attr.type)
2176+
typ, _ = self.node_type_from_base(defn, defn.info)
2177+
# TODO: defer if current node is a decorator that is not ready yet.
2178+
assert typ is not None
2179+
22122180
original_node = base_attr.node
22132181
# `original_type` can be partial if (e.g.) it is originally an
22142182
# instance variable from an `__init__` block that becomes deferred.
22152183
supertype_ready = True
2216-
if original_type is None or isinstance(original_type, PartialType):
2184+
original_type, _ = self.node_type_from_base(defn, base, name_override=name)
2185+
if original_type is None:
22172186
supertype_ready = False
22182187
if self.pass_num < self.last_pass:
22192188
# If there are passes left, defer this node until next pass,
@@ -2255,7 +2224,7 @@ def check_method_override_for_base_with_name(
22552224
# supertype is not known precisely.
22562225
if supertype_ready:
22572226
always_allow_covariant = True
2258-
self.check_setter_type_override(defn, base_attr, base)
2227+
self.check_setter_type_override(defn, base)
22592228

22602229
if isinstance(original_node, (FuncDef, OverloadedFuncDef)):
22612230
original_class_or_static = original_node.is_class or original_node.is_static
@@ -2265,41 +2234,24 @@ def check_method_override_for_base_with_name(
22652234
else:
22662235
original_class_or_static = False # a variable can't be class or static
22672236

2268-
if isinstance(original_type, FunctionLike):
2269-
original_type = self.bind_and_map_method(base_attr, original_type, defn.info, base)
2270-
if original_node and is_property(original_node):
2271-
original_type = get_property_type(original_type)
2272-
2273-
if isinstance(original_node, Var):
2274-
expanded_type = map_type_from_supertype(original_type, defn.info, base)
2275-
expanded_type = expand_self_type(
2276-
original_node, expanded_type, fill_typevars(defn.info)
2277-
)
2278-
original_type = get_proper_type(expanded_type)
2237+
typ = get_proper_type(typ)
2238+
original_type = get_proper_type(original_type)
22792239

2280-
if is_property(defn):
2281-
inner: FunctionLike | None
2282-
if isinstance(typ, FunctionLike):
2283-
inner = typ
2284-
else:
2285-
inner = self.extract_callable_type(typ, context)
2286-
if inner is not None:
2287-
typ = inner
2288-
typ = get_property_type(typ)
2289-
if (
2290-
isinstance(original_node, Var)
2291-
and not original_node.is_final
2292-
and (not original_node.is_property or original_node.is_settable_property)
2293-
and isinstance(defn, Decorator)
2294-
):
2295-
# We only give an error where no other similar errors will be given.
2296-
if not isinstance(original_type, AnyType):
2297-
self.msg.fail(
2298-
"Cannot override writeable attribute with read-only property",
2299-
# Give an error on function line to match old behaviour.
2300-
defn.func,
2301-
code=codes.OVERRIDE,
2302-
)
2240+
if (
2241+
is_property(defn)
2242+
and isinstance(original_node, Var)
2243+
and not original_node.is_final
2244+
and (not original_node.is_property or original_node.is_settable_property)
2245+
and isinstance(defn, Decorator)
2246+
):
2247+
# We only give an error where no other similar errors will be given.
2248+
if not isinstance(original_type, AnyType):
2249+
self.msg.fail(
2250+
"Cannot override writeable attribute with read-only property",
2251+
# Give an error on function line to match old behaviour.
2252+
defn.func,
2253+
code=codes.OVERRIDE,
2254+
)
23032255

23042256
if isinstance(original_type, AnyType) or isinstance(typ, AnyType):
23052257
pass
@@ -3412,7 +3364,7 @@ def get_variable_type_context(self, inferred: Var, rvalue: Expression) -> Type |
34123364
# For inference within class body, get supertype attribute as it would look on
34133365
# a class object for lambdas overriding methods, etc.
34143366
base_node = base.names[inferred.name].node
3415-
base_type, _ = self.lvalue_type_from_base(
3367+
base_type, _ = self.node_type_from_base(
34163368
inferred,
34173369
base,
34183370
is_class=is_method(base_node)
@@ -3523,7 +3475,7 @@ def check_compatibility_all_supers(self, lvalue: RefExpr, rvalue: Expression) ->
35233475
rvalue_type = self.expr_checker.accept(rvalue, lvalue_node.type)
35243476
actual_lvalue_type = lvalue_node.type
35253477
lvalue_node.type = rvalue_type
3526-
lvalue_type, _ = self.lvalue_type_from_base(lvalue_node, lvalue_node.info)
3478+
lvalue_type, _ = self.node_type_from_base(lvalue_node, lvalue_node.info)
35273479
if lvalue_node.is_inferred and not lvalue_node.explicit_self_type:
35283480
lvalue_node.type = actual_lvalue_type
35293481

@@ -3542,7 +3494,7 @@ def check_compatibility_all_supers(self, lvalue: RefExpr, rvalue: Expression) ->
35423494
if is_private(lvalue_node.name):
35433495
continue
35443496

3545-
base_type, base_node = self.lvalue_type_from_base(lvalue_node, base)
3497+
base_type, base_node = self.node_type_from_base(lvalue_node, base)
35463498
custom_setter = is_custom_settable_property(base_node)
35473499
if isinstance(base_type, PartialType):
35483500
base_type = None
@@ -3561,7 +3513,7 @@ def check_compatibility_all_supers(self, lvalue: RefExpr, rvalue: Expression) ->
35613513
# base classes are also incompatible
35623514
return
35633515
if lvalue_type and custom_setter:
3564-
base_type, _ = self.lvalue_type_from_base(
3516+
base_type, _ = self.node_type_from_base(
35653517
lvalue_node, base, setter_type=True
35663518
)
35673519
# Setter type for a custom property must be ready if
@@ -3612,26 +3564,33 @@ def check_compatibility_super(
36123564
)
36133565
return ok
36143566

3615-
def lvalue_type_from_base(
3616-
self, expr_node: Var, base: TypeInfo, setter_type: bool = False, is_class: bool = False
3567+
def node_type_from_base(
3568+
self,
3569+
node: SymbolNode,
3570+
base: TypeInfo,
3571+
*,
3572+
setter_type: bool = False,
3573+
is_class: bool = False,
3574+
name_override: str | None = None,
36173575
) -> tuple[Type | None, SymbolNode | None]:
3618-
"""Find a type for a variable name in base class.
3576+
"""Find a type for a name in base class.
36193577
36203578
Return the type found and the corresponding node defining the name or None
36213579
for both if the name is not defined in base or the node type is not known (yet).
36223580
The type returned is already properly mapped/bound to the subclass.
36233581
If setter_type is True, return setter types for settable properties (otherwise the
36243582
getter type is returned).
36253583
"""
3626-
expr_name = expr_node.name
3627-
base_var = base.names.get(expr_name)
3584+
name = name_override or node.name
3585+
base_node = base.names.get(name)
36283586

36293587
# TODO: defer current node if the superclass node is not ready.
36303588
if (
3631-
not base_var
3632-
or not base_var.type
3633-
or isinstance(base_var.type, PartialType)
3634-
and base_var.type.type is not None
3589+
not base_node
3590+
or isinstance(base_node.node, Var)
3591+
and not base_node.type
3592+
or isinstance(base_node.type, PartialType)
3593+
and base_node.type.type is not None
36353594
):
36363595
return None, None
36373596

@@ -3645,9 +3604,9 @@ def lvalue_type_from_base(
36453604
mx = MemberContext(
36463605
is_lvalue=setter_type,
36473606
is_super=False,
3648-
is_operator=mypy.checkexpr.is_operator_method(expr_name),
3607+
is_operator=mypy.checkexpr.is_operator_method(name),
36493608
original_type=self_type,
3650-
context=expr_node,
3609+
context=node,
36513610
chk=self,
36523611
suppress_errors=True,
36533612
)
@@ -3656,11 +3615,11 @@ def lvalue_type_from_base(
36563615
if is_class:
36573616
fallback = instance.type.metaclass_type or mx.named_type("builtins.type")
36583617
base_type = analyze_class_attribute_access(
3659-
instance, expr_name, mx, mcs_fallback=fallback, override_info=base
3618+
instance, name, mx, mcs_fallback=fallback, override_info=base
36603619
)
36613620
else:
3662-
base_type = analyze_instance_member_access(expr_name, instance, mx, base)
3663-
return base_type, base_var.node
3621+
base_type = analyze_instance_member_access(name, instance, mx, base)
3622+
return base_type, base_node.node
36643623

36653624
def check_compatibility_classvar_super(
36663625
self, node: Var, base: TypeInfo, base_node: Node | None
@@ -8965,29 +8924,6 @@ def is_custom_settable_property(defn: SymbolNode | None) -> bool:
89658924
return not is_same_type(get_property_type(get_proper_type(var.type)), setter_type)
89668925

89678926

8968-
def get_raw_setter_type(defn: OverloadedFuncDef | Var) -> tuple[Type, bool]:
8969-
"""Get an effective original setter type for a node.
8970-
8971-
For a variable it is simply its type. For a property it is the type
8972-
of the setter method (if not None), or the getter method (used as fallback
8973-
for the plugin generated properties).
8974-
Return the type and a flag indicating that we didn't fall back to getter.
8975-
"""
8976-
if isinstance(defn, Var):
8977-
# This function should not be called if the var is not ready.
8978-
assert defn.type is not None
8979-
return defn.type, True
8980-
first_item = defn.items[0]
8981-
assert isinstance(first_item, Decorator)
8982-
var = first_item.var
8983-
# This function may be called on non-custom properties, so we need
8984-
# to handle the situation when it is synthetic (plugin generated).
8985-
if var.setter_type is not None:
8986-
return var.setter_type, True
8987-
assert var.type is not None
8988-
return var.type, False
8989-
8990-
89918927
def get_property_type(t: ProperType) -> ProperType:
89928928
if isinstance(t, CallableType):
89938929
return get_proper_type(t.ret_type)

mypy/checkmember.py

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -559,6 +559,8 @@ def analyze_member_var_access(
559559
elif isinstance(v, MypyFile):
560560
mx.chk.module_refs.add(v.fullname)
561561
return mx.chk.expr_checker.module_type(v)
562+
elif isinstance(v, TypeVarExpr):
563+
return mx.chk.named_type("typing.TypeVar")
562564
elif (
563565
not v
564566
and name not in ["__getattr__", "__setattr__", "__getattribute__"]

test-data/unit/check-classes.test

Lines changed: 6 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -7981,25 +7981,25 @@ class Parent:
79817981
class Child(Parent):
79827982
def foo(self, val: int) -> int: # E: Signature of "foo" incompatible with supertype "Parent" \
79837983
# N: Superclass: \
7984-
# N: None \
7984+
# N: <typing special form> \
79857985
# N: Subclass: \
79867986
# N: def foo(self, val: int) -> int
79877987
return val
79887988
def bar(self, val: str) -> str: # E: Signature of "bar" incompatible with supertype "Parent" \
79897989
# N: Superclass: \
7990-
# N: None \
7990+
# N: def __init__(self) -> bar \
79917991
# N: Subclass: \
79927992
# N: def bar(self, val: str) -> str
79937993
return val
79947994
def baz(self, val: float) -> float: # E: Signature of "baz" incompatible with supertype "Parent" \
79957995
# N: Superclass: \
7996-
# N: None \
7996+
# N: Module \
79977997
# N: Subclass: \
79987998
# N: def baz(self, val: float) -> float
79997999
return val
80008000
def foobar(self) -> bool: # E: Signature of "foobar" incompatible with supertype "Parent" \
80018001
# N: Superclass: \
8002-
# N: None \
8002+
# N: TypeVar \
80038003
# N: Subclass: \
80048004
# N: def foobar(self) -> bool
80058005
return False
@@ -8012,6 +8012,8 @@ a: int = child.foo(1)
80128012
b: str = child.bar("abc")
80138013
c: float = child.baz(3.4)
80148014
d: bool = child.foobar()
8015+
[builtins fixtures/module.pyi]
8016+
[typing fixtures/typing-full.pyi]
80158017

80168018
[case testGenericTupleTypeCreation]
80178019
from typing import Generic, Tuple, TypeVar

test-data/unit/check-functions.test

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2837,8 +2837,7 @@ class not_a_decorator:
28372837
class BadChild2(Base):
28382838
@property
28392839
@not_a_decorator
2840-
def foo(self) -> int: # E: "not_a_decorator" not callable \
2841-
# E: Signature of "foo" incompatible with supertype "Base" \
2840+
def foo(self) -> int: # E: Signature of "foo" incompatible with supertype "Base" \
28422841
# N: Superclass: \
28432842
# N: int \
28442843
# N: Subclass: \

test-data/unit/check-plugin-attrs.test

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -990,10 +990,10 @@ class C(A, B): pass
990990
@attr.s
991991
class D(A): pass
992992

993-
reveal_type(A.__lt__) # N: Revealed type is "def [_AT] (self: _AT`5, other: _AT`5) -> builtins.bool"
994-
reveal_type(B.__lt__) # N: Revealed type is "def [_AT] (self: _AT`6, other: _AT`6) -> builtins.bool"
995-
reveal_type(C.__lt__) # N: Revealed type is "def [_AT] (self: _AT`7, other: _AT`7) -> builtins.bool"
996-
reveal_type(D.__lt__) # N: Revealed type is "def [_AT] (self: _AT`8, other: _AT`8) -> builtins.bool"
993+
reveal_type(A.__lt__) # N: Revealed type is "def [_AT] (self: _AT`29, other: _AT`29) -> builtins.bool"
994+
reveal_type(B.__lt__) # N: Revealed type is "def [_AT] (self: _AT`30, other: _AT`30) -> builtins.bool"
995+
reveal_type(C.__lt__) # N: Revealed type is "def [_AT] (self: _AT`31, other: _AT`31) -> builtins.bool"
996+
reveal_type(D.__lt__) # N: Revealed type is "def [_AT] (self: _AT`32, other: _AT`32) -> builtins.bool"
997997

998998
A() < A()
999999
B() < B()

test-data/unit/check-selftype.test

Lines changed: 5 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -160,12 +160,7 @@ class C(A[int]):
160160
def f(self) -> int: ...
161161

162162
class D(A[str]):
163-
def f(self) -> int: ... # E: Signature of "f" incompatible with supertype "A" \
164-
# N: Superclass: \
165-
# N: @overload \
166-
# N: def f(self) -> str \
167-
# N: Subclass: \
168-
# N: def f(self) -> int
163+
def f(self) -> int: ... # E: Return type "int" of "f" incompatible with return type "str" in supertype "A"
169164

170165
class E(A[T]):
171166
def f(self) -> int: ... # E: Signature of "f" incompatible with supertype "A" \
@@ -201,7 +196,6 @@ class I(A[int]):
201196
class J(A[int]):
202197
def f(self, arg) -> int: ... # E: Signature of "f" incompatible with supertype "A" \
203198
# N: Superclass: \
204-
# N: @overload \
205199
# N: def f(self) -> int \
206200
# N: Subclass: \
207201
# N: def f(self, arg: Any) -> int
@@ -224,12 +218,10 @@ class B(A[int]):
224218
def f(self, s: int) -> int: ...
225219

226220
class C(A[None]):
227-
def f(self, s: int) -> int: ... # E: Signature of "f" incompatible with supertype "A" \
228-
# N: Superclass: \
229-
# N: @overload \
230-
# N: def f(self, s: None) -> None \
231-
# N: Subclass: \
232-
# N: def f(self, s: int) -> int
221+
def f(self, s: int) -> int: ... # E: Return type "int" of "f" incompatible with return type "None" in supertype "A" \
222+
# E: Argument 1 of "f" is incompatible with supertype "A"; supertype defines the argument type as "None" \
223+
# N: This violates the Liskov substitution principle \
224+
# N: See https://mypy.readthedocs.io/en/stable/common_issues.html#incompatible-overrides
233225
[builtins fixtures/tuple.pyi]
234226

235227
[case testSelfTypeOverrideCompatibilityTypeVar]

0 commit comments

Comments
 (0)