Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
32 changes: 32 additions & 0 deletions mypy/checkexpr.py
Original file line number Diff line number Diff line change
Expand Up @@ -610,6 +610,13 @@ def visit_call_expr_inner(self, e: CallExpr, allow_none_return: bool = False) ->
):
member = e.callee.name
object_type = self.chk.lookup_type(e.callee.expr)
elif isinstance(e.callee, SuperExpr):
# SuperExpr is not a RefExpr, so its method cannot be identified from
# the expression node. Use the definition attached to the resolved
# callable instead, just as regular method calls use the defining class.
fullname = self.super_method_fullname(callee_type)
if fullname is not None:
object_type = self.super_method_object_type(e.callee)

if (
self.chk.options.disallow_untyped_calls
Expand Down Expand Up @@ -653,6 +660,31 @@ def visit_call_expr_inner(self, e: CallExpr, allow_none_return: bool = False) ->
self.chk.msg.does_not_return_value(callee_type, e)
return ret_type

def super_method_fullname(self, callee_type: Type) -> str | None:
"""Return the defining fullname for a method resolved through super()."""
callee_type = get_proper_type(callee_type)
if isinstance(callee_type, CallableType):
definition = callee_type.definition
return definition.fullname if definition is not None else None
if isinstance(callee_type, Overloaded):
for item in callee_type.items:
if item.definition is not None:
return item.definition.fullname
return None

def super_method_object_type(self, e: SuperExpr) -> Type | None:
"""Return the object type passed to the super() proxy for plugin contexts."""
if len(e.call.args) == 2 and self.chk.has_type(e.call.args[1]):
return self.chk.lookup_type(e.call.args[1])
if e.info is not None:
method = self.chk.scope.current_function()
if method is not None and method.arguments:
instance_type = method.arguments[0].variable.type
if instance_type is not None:
return instance_type
return fill_typevars(e.info)
return None

def check_str_format_call(self, e: CallExpr) -> None:
"""More precise type checking for str.format() calls on literals and folded constants."""
assert isinstance(e.callee, MemberExpr)
Expand Down
4 changes: 4 additions & 0 deletions test-data/unit/check-custom-plugin.test
Original file line number Diff line number Diff line change
Expand Up @@ -572,6 +572,10 @@ foo[4] = 5
for x in foo:
reveal_type(x) # N: Revealed type is "builtins.int"

class Bar(Foo):
def super_m(self) -> None:
reveal_type(super().m(2)) # N: Revealed type is "builtins.int"

[file mypy.ini]
\[mypy]
plugins=<ROOT>/test-data/unit/plugins/method_sig_hook.py
Expand Down
Loading