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
2 changes: 1 addition & 1 deletion mypy/cache.py
Original file line number Diff line number Diff line change
Expand Up @@ -72,7 +72,7 @@
from mypy_extensions import u8

# High-level cache layout format
CACHE_VERSION: Final = 11
CACHE_VERSION: Final = 12

# Type used internally to represent errors:
# (path, line, column, end_line, end_column, severity, message, code)
Expand Down
3 changes: 2 additions & 1 deletion mypy/checker.py
Original file line number Diff line number Diff line change
Expand Up @@ -246,6 +246,7 @@ def __init__(self) -> None:
false_only,
fixup_partial_type,
function_type,
has_any_type,
is_literal_type_like,
is_singleton_equality_type,
is_singleton_identity_type,
Expand Down Expand Up @@ -5970,7 +5971,7 @@ def check_untyped_after_decorator(self, typ: Type, func: FuncDef) -> None:
if not self.options.disallow_any_decorated or self.is_stub or self.current_node_deferred:
return

if mypy.checkexpr.has_any_type(typ):
if has_any_type(typ):
self.msg.untyped_decorated_function(typ, func)

def check_async_with_item(
Expand Down
111 changes: 52 additions & 59 deletions mypy/checkexpr.py
Original file line number Diff line number Diff line change
Expand Up @@ -152,6 +152,7 @@
freeze_all_type_vars,
get_all_type_vars,
get_type_vars,
has_any_type,
is_literal_type_like,
make_simplified_union,
true_only,
Expand Down Expand Up @@ -2874,6 +2875,7 @@ def check_overload_call(
callable_name,
object_type,
none_type_var_overlap,
callee.bound_args,
context,
)
except TooManyUnions:
Expand Down Expand Up @@ -2902,6 +2904,7 @@ def check_overload_call(
arg_names,
callable_name,
object_type,
callee.bound_args,
context,
)
# If any of checks succeed, perform deprecation tests and stop early.
Expand Down Expand Up @@ -2980,7 +2983,7 @@ def plausible_overload_call_targets(
arg_kinds: list[ArgKind],
arg_names: Sequence[str | None] | None,
overload: Overloaded,
) -> list[CallableType]:
) -> list[tuple[int, CallableType]]:
"""Returns all overload call targets that having matching argument counts.

If the given args contains a star-arg (*arg or **kwarg argument, except for
Expand All @@ -2990,16 +2993,19 @@ def plausible_overload_call_targets(
The only exception is if the starred argument is something like a Tuple or a
NamedTuple, which has a definitive "shape". If so, we don't move the corresponding
alternative to the front since we can infer a more precise match using the original
order."""
order.

The return also contains the original overload index for each plausible target.
"""

def has_shape(typ: Type) -> bool:
typ = get_proper_type(typ)
return isinstance(typ, (TupleType, TypedDictType)) or (
isinstance(typ, Instance) and typ.type.is_named_tuple
)

matches: list[CallableType] = []
star_matches: list[CallableType] = []
matches: list[tuple[int, CallableType]] = []
star_matches: list[tuple[int, CallableType]] = []

args_have_var_arg = False
args_have_kw_arg = False
Expand All @@ -3009,7 +3015,7 @@ def has_shape(typ: Type) -> bool:
if kind == ARG_STAR2 and not has_shape(typ):
args_have_kw_arg = True

for typ in overload.items:
for idx, typ in enumerate(overload.items):
formal_to_actual = map_actuals_to_formals(
arg_kinds, arg_names, typ.arg_kinds, typ.arg_names, lambda i: arg_types[i]
)
Expand All @@ -3020,28 +3026,29 @@ def has_shape(typ: Type) -> bool:
# is safe: it will be filtered out later.
# Unlike other var-args signatures, ParamSpec produces essentially
# a fixed signature, so there's no need to push them to the top.
matches.append(typ)
matches.append((idx, typ))
elif self.check_argument_count(
typ, arg_types, arg_kinds, arg_names, formal_to_actual, None
):
if args_have_var_arg and typ.is_var_arg:
star_matches.append(typ)
star_matches.append((idx, typ))
elif args_have_kw_arg and typ.is_kw_arg:
star_matches.append(typ)
star_matches.append((idx, typ))
else:
matches.append(typ)
matches.append((idx, typ))

return star_matches + matches

def infer_overload_return_type(
self,
plausible_targets: list[CallableType],
plausible_targets: list[tuple[int, CallableType]],
args: list[Expression],
arg_types: list[Type],
arg_kinds: list[ArgKind],
arg_names: Sequence[str | None] | None,
callable_name: str | None,
object_type: Type | None,
bound_args: list[Type] | None,
context: Context,
) -> tuple[Type, Type] | None:
"""Attempts to find the first matching callable from the given list.
Expand All @@ -3051,16 +3058,17 @@ def infer_overload_return_type(
If multiple targets match due to ambiguous Any parameters, returns (AnyType, AnyType).
If no targets match, returns None.

Assumes all of the given targets have argument counts compatible with the caller.
Assumes all the given targets have argument counts compatible with the caller.
"""

matches: list[CallableType] = []
matches: list[tuple[int, CallableType]] = []
return_types: list[Type] = []
inferred_types: list[Type] = []
args_contain_any = any(map(has_any_type, arg_types))
# bound_arg are recorded only if the original object type contained any.
args_contain_any = any(map(has_any_type, arg_types)) or bound_args is not None
type_maps: list[dict[Expression, Type]] = []

for typ in plausible_targets:
for idx, typ in plausible_targets:
assert self.msg is self.chk.msg
with self.msg.filter_errors(filter_revealed_type=True) as w:
with self.chk.local_type_map as m:
Expand All @@ -3084,16 +3092,18 @@ def infer_overload_return_type(
if isinstance(p_infer_type, CallableType):
# Prefer inferred types if possible, this will avoid false triggers for
# Any-ambiguity caused by arguments with Any passed to generic overloads.
matches.append(p_infer_type)
matches.append((idx, p_infer_type))
else:
matches.append(typ)
matches.append((idx, typ))
return_types.append(ret_type)
inferred_types.append(infer_type)
type_maps.append(m)

if not matches:
return None
elif any_causes_overload_ambiguity(matches, return_types, arg_types, arg_kinds, arg_names):
elif any_causes_overload_ambiguity(
matches, return_types, arg_types, arg_kinds, arg_names, bound_args
):
# An argument of type or containing the type 'Any' caused ambiguity.
# We try returning a precise type if we can. If not, we give up and just return 'Any'.
if all_same_types(return_types):
Expand All @@ -3119,7 +3129,7 @@ def infer_overload_return_type(

def overload_erased_call_targets(
self,
plausible_targets: list[CallableType],
plausible_targets: list[tuple[int, CallableType]],
arg_types: list[Type],
arg_kinds: list[ArgKind],
arg_names: Sequence[str | None] | None,
Expand All @@ -3131,15 +3141,15 @@ def overload_erased_call_targets(
Assumes all of the given targets have argument counts compatible with the caller.
"""
matches: list[CallableType] = []
for typ in plausible_targets:
for _, typ in plausible_targets:
if self.erased_signature_similarity(
arg_types, arg_kinds, arg_names, args, typ, context
):
matches.append(typ)
return matches

def possible_none_type_var_overlap(
self, arg_types: list[Type], plausible_targets: list[CallableType]
self, arg_types: list[Type], plausible_targets: list[tuple[int, CallableType]]
) -> bool:
"""Heuristic to determine whether we need to try forcing union math.

Expand All @@ -3166,26 +3176,28 @@ def foo(x: T) -> list[T]: ...
if not has_optional_arg:
return False

min_prefix = min(len(c.arg_types) for c in plausible_targets)
min_prefix = min(len(c.arg_types) for _, c in plausible_targets)
for i in range(min_prefix):
if any(
isinstance(get_proper_type(c.arg_types[i]), NoneType) for c in plausible_targets
isinstance(get_proper_type(c.arg_types[i]), NoneType) for _, c in plausible_targets
) and any(
isinstance(get_proper_type(c.arg_types[i]), TypeVarType) for c in plausible_targets
isinstance(get_proper_type(c.arg_types[i]), TypeVarType)
for _, c in plausible_targets
):
return True
return False

def union_overload_result(
self,
plausible_targets: list[CallableType],
plausible_targets: list[tuple[int, CallableType]],
args: list[Expression],
arg_types: list[Type],
arg_kinds: list[ArgKind],
arg_names: Sequence[str | None] | None,
callable_name: str | None,
object_type: Type | None,
none_type_var_overlap: bool,
bound_args: list[Type] | None,
context: Context,
level: int = 0,
) -> list[tuple[Type, Type]] | None:
Expand All @@ -3195,7 +3207,7 @@ def union_overload_result(
Return a list of (<return type>, <inferred variant type>) if call succeeds for every
item of the desctructured union. Returns None if there is no match.
"""
# Step 1: If we are already too deep, then stop immediately. Otherwise mypy might
# Step 1: If we are already too deep, then stop immediately. Otherwise, mypy might
# hang for long time because of a weird overload call. The caller will get
# the exception and generate an appropriate note message, if needed.
if level >= MAX_UNIONS:
Expand All @@ -3217,6 +3229,7 @@ def union_overload_result(
arg_names,
callable_name,
object_type,
bound_args,
context,
)
if res is not None:
Expand All @@ -3235,6 +3248,7 @@ def union_overload_result(
arg_names,
callable_name,
object_type,
bound_args,
context,
)
if direct is not None and not isinstance(
Expand All @@ -3260,6 +3274,7 @@ def union_overload_result(
callable_name,
object_type,
none_type_var_overlap,
bound_args,
context,
level + 1,
)
Expand Down Expand Up @@ -6684,37 +6699,6 @@ def try_parse_as_type_expression(self, maybe_type_expr: Expression) -> Type | No
return None


def has_any_type(t: Type, ignore_in_type_obj: bool = False) -> bool:
"""Whether t contains an Any type"""
return t.accept(HasAnyType(ignore_in_type_obj))


class HasAnyType(types.BoolTypeQuery):
def __init__(self, ignore_in_type_obj: bool) -> None:
super().__init__(types.ANY_STRATEGY)
self.ignore_in_type_obj = ignore_in_type_obj

def visit_any(self, t: AnyType) -> bool:
return t.type_of_any != TypeOfAny.special_form # special forms are not real Any types

def visit_callable_type(self, t: CallableType) -> bool:
if self.ignore_in_type_obj and t.is_type_obj():
return False
return super().visit_callable_type(t)

def visit_type_var(self, t: TypeVarType) -> bool:
default = [t.default] if t.has_default() else []
return self.query_types([t.upper_bound, *default] + t.values)

def visit_param_spec(self, t: ParamSpecType) -> bool:
default = [t.default] if t.has_default() else []
return self.query_types([t.upper_bound, *default, t.prefix])

def visit_type_var_tuple(self, t: TypeVarTupleType) -> bool:
default = [t.default] if t.has_default() else []
return self.query_types([t.upper_bound, *default])


def has_coroutine_decorator(t: Type) -> bool:
"""Whether t came from a function decorated with `@coroutine`."""
t = get_proper_type(t)
Expand Down Expand Up @@ -6902,11 +6886,12 @@ def is_typetype_like(typ: ProperType) -> bool:


def any_causes_overload_ambiguity(
items: list[CallableType],
items: list[tuple[int, CallableType]],
return_types: list[Type],
arg_types: list[Type],
arg_kinds: list[ArgKind],
arg_names: Sequence[str | None] | None,
bound_args: list[Type] | None,
) -> bool:
"""May an argument containing 'Any' cause ambiguous result type on call to overloaded function?

Expand All @@ -6916,9 +6901,11 @@ def any_causes_overload_ambiguity(

Args:
items: Overload items matching the actual arguments
return_types: Corresponding inferred return type for each item
arg_types: Actual argument types
arg_kinds: Actual argument kinds
arg_names: Actual argument names
bound_args: Full list of self-types bound (if overload is a method)
"""
if all_same_types(return_types):
return False
Expand All @@ -6927,7 +6914,7 @@ def any_causes_overload_ambiguity(
map_formals_to_actuals(
arg_kinds, arg_names, item.arg_kinds, item.arg_names, lambda i: arg_types[i]
)
for item in items
for _, item in items
]

for arg_idx, arg_type in enumerate(arg_types):
Expand All @@ -6944,7 +6931,7 @@ def any_causes_overload_ambiguity(
matching_returns = []
matching_formals = []
for item_idx, formals in matching_formals_unfiltered:
matched_callable = items[item_idx]
_, matched_callable = items[item_idx]
matching_returns.append(matched_callable.ret_type)

# Note: if an actual maps to multiple formals of differing types within
Expand All @@ -6956,6 +6943,12 @@ def any_causes_overload_ambiguity(
if not all_same_types(matching_formals) and not all_same_types(matching_returns):
# Any maps to multiple different types, and the return types of these items differ.
return True

# If the original object type for methods contained Any, check the self-types as well.
if bound_args is not None:
matching_bound = [bound_args[idx] for idx, _ in items]
if not all_same_types(matching_bound):
return True
return False


Expand Down
2 changes: 1 addition & 1 deletion mypy/copytype.py
Original file line number Diff line number Diff line change
Expand Up @@ -120,7 +120,7 @@ def visit_union_type(self, t: UnionType) -> ProperType:
return self.copy_common(t, UnionType(t.items))

def visit_overloaded(self, t: Overloaded) -> ProperType:
return self.copy_common(t, Overloaded(items=t.items))
return self.copy_common(t, Overloaded(items=t.items, bound_args=t.bound_args))

def visit_type_type(self, t: TypeType) -> ProperType:
# Use cast since the type annotations in TypeType are imprecise.
Expand Down
2 changes: 1 addition & 1 deletion mypy/expandtype.py
Original file line number Diff line number Diff line change
Expand Up @@ -508,7 +508,7 @@ def visit_overloaded(self, t: Overloaded) -> Type:
assert isinstance(new_item, ProperType)
assert isinstance(new_item, CallableType)
items.append(new_item)
return Overloaded(items)
return Overloaded(items, t.bound_args)

def expand_type_list_with_unpack(self, typs: list[Type]) -> list[Type]:
"""Expands a list of types that has an unpack."""
Expand Down
3 changes: 1 addition & 2 deletions mypy/suggestions.py
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,6 @@

from mypy.argmap import map_actuals_to_formals
from mypy.build import Graph, State
from mypy.checkexpr import has_any_type
from mypy.find_sources import InvalidSourceList, SourceFinder
from mypy.join import join_type_list
from mypy.meet import meet_type_list
Expand All @@ -60,7 +59,7 @@
from mypy.server.update import FineGrainedBuildManager
from mypy.state import state
from mypy.traverser import TraverserVisitor
from mypy.typeops import bind_self, make_simplified_union
from mypy.typeops import bind_self, has_any_type, make_simplified_union
from mypy.types import (
AnyType,
CallableType,
Expand Down
Loading
Loading