Skip to content
Open
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
30 changes: 24 additions & 6 deletions mypy/checkexpr.py
Original file line number Diff line number Diff line change
Expand Up @@ -1420,7 +1420,11 @@ def is_generic_decorator_overload_call(
return None

def handle_decorator_overload_call(
self, callee_type: CallableType, overloaded: Overloaded, ctx: Context
self,
callee_type: CallableType,
overloaded: Overloaded,
ctx: Context,
callee_is_overload_item: bool,
) -> tuple[Type, Type] | None:
"""Type-check application of a generic callable to an overload.

Expand All @@ -1432,7 +1436,9 @@ def handle_decorator_overload_call(
for item in overloaded.items:
arg = TempNode(typ=item)
with self.msg.filter_errors() as err:
item_result, inferred_arg = self.check_call(callee_type, [arg], [ARG_POS], ctx)
item_result, inferred_arg = self.check_call(
callee_type, [arg], [ARG_POS], ctx, is_overload_item=callee_is_overload_item
)
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

callee_is_overload_item in this method doesn't actually activate any code paths. Its addition is for logical consistency and future-proofing any more cases handled here:

mypy/mypy/checkexpr.py

Lines 1403 to 1415 in 054f721

def is_generic_decorator_overload_call(
self, callee_type: CallableType, args: list[Expression]
) -> Overloaded | None:
"""Check if this looks like an application of a generic function to overload argument."""
assert callee_type.variables
if len(callee_type.arg_types) != 1 or len(args) != 1:
# TODO: can we handle more general cases?
return None
if not isinstance(get_proper_type(callee_type.arg_types[0]), CallableType):
return None
if not isinstance(get_proper_type(callee_type.ret_type), CallableType):
return None
with self.chk.local_type_map:

The test for future-proofing is this one:

[case testDeprecatedOverloadedClassConstructorDecoratingOverloadedFunction]

if err.has_new_errors():
# This overload doesn't match.
continue
Expand Down Expand Up @@ -1538,6 +1544,7 @@ def check_call(
callable_name: str | None = None,
object_type: Type | None = None,
original_type: Type | None = None,
is_overload_item: bool = False,
) -> tuple[Type, Type]:
"""Type check a call.

Expand All @@ -1558,6 +1565,7 @@ def check_call(
or None if unavailable (examples: 'builtins.open', 'typing.Mapping.get')
object_type: If callable_name refers to a method, the type of the object
on which the method is being called
is_overload_item: Whether this check is for an individual overload item
"""
callee = get_proper_type(callee)

Expand All @@ -1568,7 +1576,7 @@ def check_call(
# Special casing for inline application of generic callables to overloads.
# Supporting general case would be tricky, but this should cover 95% of cases.
overloaded_result = self.handle_decorator_overload_call(
callee, overloaded, context
callee, overloaded, context, is_overload_item
)
if overloaded_result is not None:
return overloaded_result
Expand All @@ -1582,6 +1590,7 @@ def check_call(
callable_node,
callable_name,
object_type,
is_overload_item,
)
elif isinstance(callee, Overloaded):
return self.check_overload_call(
Expand Down Expand Up @@ -1659,21 +1668,29 @@ def check_callable_call(
callable_node: Expression | None,
callable_name: str | None,
object_type: Type | None,
is_overload_item: bool = False,
) -> tuple[Type, Type]:
"""Type check a call that targets a callable value.

See the docstring of check_call for more information.
"""
# Check implicit calls to deprecated class constructors.
# Only the non-overload case is handled here. Overloaded constructors are handled
# separately during overload resolution.
if (not is_overload_item) and callee.is_type_obj():
self.chk.warn_deprecated(callee.definition, context)

# Always unpack **kwargs before checking a call.
callee = callee.with_unpacked_kwargs().with_normalized_var_args()
if callable_name is None and callee.name:
callable_name = callee.name
ret_type = get_proper_type(callee.ret_type)
if callee.is_type_obj() and isinstance(ret_type, Instance):
callable_name = ret_type.type.fullname
if isinstance(callable_node, RefExpr) and callable_node.fullname in ENUM_BASES:
# An Enum() call that failed SemanticAnalyzerPass2.check_enum_call().
return callee.ret_type, callee
if isinstance(callable_node, RefExpr) and (callable_node.fullname in ENUM_BASES):
if callable_node.fullname in ENUM_BASES:
# An Enum() call that failed SemanticAnalyzerPass2.check_enum_call().
return callee.ret_type, callee

if (
callee.is_type_obj()
Expand Down Expand Up @@ -2910,6 +2927,7 @@ def infer_overload_return_type(
context=context,
callable_name=callable_name,
object_type=object_type,
is_overload_item=True,
)
is_match = not w.has_new_errors()
if is_match:
Expand Down
Loading